diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08a3264 --- /dev/null +++ b/.gitignore @@ -0,0 +1,95 @@ +# Gradle +build +.gradle + +testdata/ +# Java gitignore # +.class +.log + +# Package Files # + +*.war +*.ear + +#hsf files +configuration + +# maven gitignore# +target/** + +.svn/ + +# intelliJ.gitignore # +.idea +*.iml +*.ipr +*.iws + + +# Eclipse git ignore# +*.pydevproject +.project +.metadata +bin/** +*/bin/** +tmp/** +tmp/**/* +configuration/** +*.tmp +*.bak +*.orig +*.swp +*~.nib +.classpath +.settings/ +.loadpath +.fileTable* +.cache + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +#log +*.log +*.log.* + +# Windows Thumbs.db +*.db + +# OSX +.DS_Store + +# sass gitignore# +.sass-cache +.idea + +# tcc_coverage +coverage.ec + + + +config.client.* + +temp/ +*.pid +*.orig + +hsf.configuration/ + +# code coverage report +*.ec + +#hsf test +*.instance +out +!/p3c-idea/src/main/kotlin/com/alibaba/smartfox/work/tools/aone/ui/AoneBranchView.kt diff --git a/README.md b/README.md index 72b3d0b..12b58d0 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,35 @@ -## 代码规约插件全球首发 -![倒计时_](https://gw.alicdn.com/tfscom/TB1il8pf6ihSKJjy0FeXXbJtpXa.jpg) -10月14日杭州云栖大会上会,代码规约插件及源码,将在杭州云栖大会上[研发效能峰会](https://yunqi.aliyun.com/2017/hangzhou/meeting?day=day4&theme=all&meeting=detail1433 )的首发仪式上全球首发。 +Java代码规约扫描插件已在[研发协同RDC](https://rdc-test.aliyun.com)(云效公有云解决方案)的测试功能中实现(设置->测试服务->阿里巴巴Java代码规约)。 -过去的整整一年中,阿里内部经历过无数次针锋相对的讨论,与实战经验相结合,系统性地从编程,数据库,异常日志,工程结构,安全,单元测试(new)六个维度总结做为Java合格开发者必备的技术素养。只是文档让开发同学遵守是非常困难的,阿里研发一套自动检测插件——代码规约扫描插件,已在[研发协同RDC](https://rdc-test.aliyun.com/)(云效公有云解决方案)的实验室功能中实现。 +# P3C -[![alt text](https://gw.alicdn.com/tfscom/TB13OnghlUSMeJjy1zkXXaWmpXa.png "title")](https://rdc-test.aliyun.com) -点图可在线扫描代码合规情况 +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -“开发手册”主要作者孤尽在10月13日的云栖大会上分享“从推动的Java的代码规约谈技术人成长的有所为”,欢迎大家到场零距离沟通。 +## Preface +> We are pleased to present Alibaba Java Coding Guidelines, which consolidates the best programming practices over the years from Alibaba Group's technical teams. A vast number of Java programming teams impose demanding requirements on code quality across projects as we encourage reuse and better understanding of each other's programs. We have seen many programming problems in the past. For example, defective database table structures and index designs may cause software architecture flaws and performance risks. Yet as another example, confusing code structures make it difficult to maintain. Furthermore, vulnerable code without authentication is prone to hackers’ attacks. To address those kinds of problems, we developed this document for Java developers in Alibaba. + +For more information please refer the *Alibaba Java Coding Guidelines*: +- 中文版: *[阿里巴巴Java开发手册](https://github.com/alibaba/p3c/blob/master/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E7%BB%88%E6%9E%81%E7%89%88%EF%BC%89.pdf)* +- English Version: *[Alibaba Java Coding Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines)* -电子工业出版社也将在一年后出版孤尽参与编写的“码出高效 - 阿里巴巴的Java开发手册详解”这本书。 +## Introduction +The project consists of 3 parts: +- [PMD implementations](p3c-pmd) +- [IntelliJ IDEA plugin](idea-plugin) +- [Eclipse plugin](eclipse-plugin) -代码规约最新更新消息关注“代码规约”微信公众号: +## Rules +49 rules are realized based on PMD, please refer the P3C-PMD documentation for more detailed information. 4 rules implemented within IDE plugins (IDEA and Eclipse) as following: -![代码规约](https://gw.alicdn.com/tfscom/TB1x5WJaGmgSKJjSsphXXcy1VXa.jpg) +- ``[Mandatory]`` Using a deprecated class or method is prohibited. + Note: For example, decode(String source, String encode) should be used instead of the deprecated method decode(String encodeStr). Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to check out what its new implementation is. + +- ``[Mandatory]`` An overridden method from an interface or abstract class must be marked with @Override annotation. + Counter example: For getObject() and get0bject(), the first one has a letter 'O', and the second one has a number '0'. To accurately determine whether the overriding is successful, an @Override annotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately. + +- ``[Mandatory]`` A static field or method should be directly referred by its class name instead of its corresponding object name. + +- ``[Mandatory]`` The usage of hashCode and equals should follow: + 1. Override hashCode if equals is overridden. + 2. These two methods must be overridden for Set since they are used to ensure that no duplicate object will be inserted in Set. + 3. These two methods must be overridden if self-defined object is used as the key of Map. + Note: String can be used as the key of Map since these two methods have been rewritten. \ No newline at end of file diff --git a/eclipse-plugin/.gitignore b/eclipse-plugin/.gitignore new file mode 100644 index 0000000..af53b90 --- /dev/null +++ b/eclipse-plugin/.gitignore @@ -0,0 +1,96 @@ +# Gradle +build +.gradle + +testdata/ +# Java gitignore # +.class +.log + +# Package Files # + +*.war +*.ear + +#hsf files +configuration + +# maven gitignore# +target/** + +.svn/ + +# intelliJ.gitignore # +.idea +*.iml +*.ipr +*.iws +*.bat + +# Eclipse git ignore# +*.pydevproject +.project +.metadata +bin/** +*/bin/** +tmp/** +tmp/**/* +configuration/** +*.tmp +*.bak +*.orig +*.swp +*~.nib +.classpath +.settings/ +.loadpath +.fileTable* +.cache + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +#log +*.log +*.log.* + +# Windows Thumbs.db +*.db + +# OSX +.DS_Store + +# sass gitignore# +.sass-cache +.idea + +# tcc_coverage +coverage.ec + + + +config.client.* + +temp/ +*.pid +*.orig + +hsf.configuration/ + +# code coverage report +*.ec + +#hsf test +*.instance +**/target +.pmd +**/.pmd diff --git a/eclipse-plugin/README.md b/eclipse-plugin/README.md new file mode 100644 index 0000000..f89c23b --- /dev/null +++ b/eclipse-plugin/README.md @@ -0,0 +1,31 @@ +# Eclipse Plugin +--- +## Prepare +- Eclipse Juno+ +- maven3.+ +- JDK 1.7+ + +## Build +``` +mvn -U clean install +``` + +## Install +1. Help >> Install New Software +then enter this update site URL [https://p3c.alibaba.com/plugin/eclipse/update](https://p3c.alibaba.com/plugin/eclipse/update) + +![Install Plugin](doc/images/install.png) + +2. Follow the wizard, restart Eclipse to take effect after install success. + +## Use +1. Switch language + + ![Switch language](doc/images/eclipse_switch_language.png) + +2. Code Analyze + + ![Analyze](doc/images/eclipse_analyze.png) + + ![Analyze](doc/images/analyze_result.png) + \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/build.properties b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/build.properties new file mode 100644 index 0000000..1dceaa6 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/build.properties @@ -0,0 +1,7 @@ +bin.includes = feature.xml,\ + feature.properties,\ + smartfox.png +src.includes = build.properties,\ + feature.properties,\ + feature.xml,\ + smartfox.png diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.properties b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.properties new file mode 100644 index 0000000..5bd3a31 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.properties @@ -0,0 +1,26 @@ +feature.label = Ali-CodeAnalysis +feature.provider_name = Alibaba +feature.update_site_name = Alibaba IDE Portal + +description.text = Alibaba Java Coding Guidelines +description.url = https://github.com/alibaba/p3c + +copyright.text =\ +Copyright 2017 Alibaba Java Coding Guidelines + +license.url = https://github.com/alibaba/p3c +license.text =\ + Copyright 1999-2017 Alibaba Group. \n\ + \n\ + Licensed under the Apache License, Version 2.0 (the "License"); \n\ + you may not use this file except in compliance with the License. \n\ + You may obtain a copy of the License at \n\ + \n\ + http://www.apache.org/licenses/LICENSE-2.0 \n\ + \n\ + Unless required by applicable law or agreed to in writing, software \n\ + distributed under the License is distributed on an "AS IS" BASIS, \n\ + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n\ + See the License for the specific language governing permissions and \n\ + limitations under the License. \n\ + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.xml new file mode 100644 index 0000000..56c09eb --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.xml @@ -0,0 +1,46 @@ + + + + + Alibaba Java Coding Guidelines + + + + %copyright.text + + + + %license.text + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/pom.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/pom.xml new file mode 100644 index 0000000..90eb846 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + com.alibaba.smartfox.eclipse + smartfox-eclipse + 1.0.0-SNAPSHOT + + com.alibaba.smartfox.eclipse.feature + eclipse-feature + 2017 + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/smartfox.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/smartfox.png new file mode 100644 index 0000000..75a2c1b Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.feature/smartfox.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/META-INF/MANIFEST.MF b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/META-INF/MANIFEST.MF new file mode 100644 index 0000000..0dd9db8 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/META-INF/MANIFEST.MF @@ -0,0 +1,60 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: com.alibaba.smartfox.eclipse.plugin +Bundle-SymbolicName: com.alibaba.smartfox.eclipse.plugin;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: com.alibaba.smartfox.eclipse.SmartfoxActivator +Bundle-Vendor: Alibaba +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.jdt.core, + org.eclipse.ui.ide, + org.eclipse.ui.views, + org.eclipse.core.resources, + org.eclipse.jface.text, + org.eclipse.ui.workbench.texteditor, + org.eclipse.ltk.core.refactoring, + org.eclipse.jdt.ui, + org.eclipse.core.filebuffers, + org.eclipse.equinox.p2.core, + org.eclipse.equinox.p2.engine, + org.eclipse.equinox.p2.operations, + org.eclipse.equinox.p2.metadata.repository, + org.eclipse.equinox.p2.ui, + org.eclipse.equinox.p2.metadata +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ActivationPolicy: lazy +Bundle-ClassPath: target/lib/antlr-runtime.jar, + target/lib/antlr4-runtime.jar, + target/lib/asm.jar, + target/lib/commons-io.jar, + target/lib/commons-lang3.jar, + target/lib/gson.jar, + target/lib/javacc.jar, + target/lib/jaxen.jar, + target/lib/jcommander.jar, + target/lib/log4j.jar, + target/lib/pmd-core.jar, + target/lib/pmd-java.jar, + target/lib/pmd-javascript.jar, + target/lib/pmd-vm.jar, + target/lib/rhino.jar, + target/lib/saxon-dom.jar, + target/lib/saxon.jar, + target/lib/p3c-pmd.jar, + target/lib/kotlin-stdlib.jar, + target/lib/statistics-client.jar, + target/lib/fastjson.jar, + target/lib/guava.jar, + target/classes/, + . +Bundle-Localization: plugin +Export-Package: com.alibaba.smartfox.eclipse, + com.alibaba.smartfox.eclipse.handler, + com.alibaba.smartfox.eclipse.job, + com.alibaba.smartfox.eclipse.marker, + com.alibaba.smartfox.eclipse.pmd, + com.alibaba.smartfox.eclipse.pmd.rule, + com.alibaba.smartfox.eclipse.ui, + com.alibaba.smartfox.eclipse.ui.pmd, + com.alibaba.smartfox.eclipse.util diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/build.properties b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/build.properties new file mode 100644 index 0000000..b305b36 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/build.properties @@ -0,0 +1,14 @@ +bin.includes = .,\ + META-INF/,\ + plugin.xml,\ + icons/,\ + target/lib/ +src.includes = icons/,\ + META-INF/,\ + plugin.xml,\ + messages.properties,\ + target/lib/,\ + src/main/kotlin,\ + pom.xml +source.. = src/main/java/,src/main/resources +output.. = target/classes/ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.gif b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.gif new file mode 100644 index 0000000..2cd9c54 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.gif differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.png new file mode 100644 index 0000000..8c5c1a0 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/clear.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/quickfixBulb.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/quickfixBulb.png new file mode 100644 index 0000000..ecc6c16 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/quickfixBulb.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/rules.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/rules.png new file mode 100644 index 0000000..9990f5f Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/actions/rules.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/ali-ide-run.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/ali-ide-run.png new file mode 100644 index 0000000..754649e Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/ali-ide-run.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/language.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/language.png new file mode 100644 index 0000000..b267454 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/language.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/blocker.gif b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/blocker.gif new file mode 100644 index 0000000..2ff6678 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/blocker.gif differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/class_obj.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/class_obj.png new file mode 100644 index 0000000..bc915da Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/class_obj.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/critical.gif b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/critical.gif new file mode 100644 index 0000000..a01bb24 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/critical.gif differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/major.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/major.png new file mode 100644 index 0000000..87adef9 Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/major.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/smartfox_logo.png b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/smartfox_logo.png new file mode 100644 index 0000000..4a56fac Binary files /dev/null and b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/icons/view/smartfox_logo.png differ diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/plugin.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/plugin.xml new file mode 100644 index 0000000..358581b --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/plugin.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/pom.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/pom.xml new file mode 100644 index 0000000..14e7644 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + + com.alibaba.smartfox.eclipse + smartfox-eclipse + 1.0.0-SNAPSHOT + + com.alibaba.smartfox.eclipse.plugin + eclipse-plugin + 2017 + + false + + + + log4j + log4j + 1.2.17 + + + com.alibaba.p3c + p3c-pmd + 1.3.0 + + + org.jetbrains.kotlin + kotlin-stdlib + + + com.google.guava + guava + 20.0 + + + + src/main/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + + compile + compile + + compile + + + + + test-compile + test-compile + + test-compile + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.7 + + true + false + ${project.build.directory}/lib + + p2.eclipse-plugin,apex + com.alibaba.smartfox.eclipse.plugin + + false + + + + get-dependencies + process-sources + + copy-dependencies + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-dependency-plugin + + [0,) + + copy-dependencies + + + + + + + + + + + + + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/QuickFix.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/QuickFix.kt new file mode 100644 index 0000000..51e02aa --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/QuickFix.kt @@ -0,0 +1,119 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse + +import com.alibaba.p3c.pmd.lang.java.rule.constant.UpperEllRule +import com.alibaba.p3c.pmd.lang.java.rule.oop.EqualsAvoidNullRule +import com.alibaba.smartfox.eclipse.ui.InspectionResults +import com.alibaba.smartfox.eclipse.util.getRule +import com.google.common.io.Files +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IMarker +import org.eclipse.core.runtime.NullProgressMonitor +import org.eclipse.ltk.core.refactoring.TextFileChange +import org.eclipse.text.edits.ReplaceEdit +import org.eclipse.ui.IMarkerResolution +import org.eclipse.ui.IMarkerResolutionGenerator +import java.nio.charset.Charset + +/** + * + * + * @author caikang + * @date 2017/06/14 + */ +class QuickFixGenerator : IMarkerResolutionGenerator { + override fun getResolutions(marker: IMarker): Array { + if (!marker.exists()) { + return emptyArray() + } + val rule = marker.getRule() + val quickFix = quickFixes[rule.name] ?: return emptyArray() + return arrayOf(quickFix) + } + + companion object { + val quickFixes = mapOf(UpperEllRule::class.java.simpleName to UpperEllQuickFix, + EqualsAvoidNullRule::class.java.simpleName to EqualsAvoidNullQuickFix) + } +} + +interface RunWithoutViewRefresh : IMarkerResolution { + fun run(marker: IMarker, refresh: Boolean) + + override fun run(marker: IMarker) { + run(marker, true) + } +} + +abstract class BaseQuickFix : RunWithoutViewRefresh { + override fun run(marker: IMarker, refresh: Boolean) { + if (!marker.exists()) { + return + } + val file = marker.resource as IFile + doRun(marker, file) + marker.delete() + if (refresh) { + InspectionResults.removeMarker(marker) + } + } + + abstract fun doRun(marker: IMarker, file: IFile) +} + +object UpperEllQuickFix : BaseQuickFix() { + override fun doRun(marker: IMarker, file: IFile) { + val offset = marker.getAttribute(IMarker.CHAR_START, 0) + val end = marker.getAttribute(IMarker.CHAR_END, 0) + val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) + val replaceString = content.substring(offset, end + 1).replace("l", "L") + val edit = ReplaceEdit(offset, replaceString.length, replaceString) + val change = TextFileChange("", file) + change.edit = edit + change.perform(NullProgressMonitor()) + } + + override fun getLabel(): String { + return "Replace 'l' to 'L'." + } + +} + +object EqualsAvoidNullQuickFix : BaseQuickFix() { + val equalsName = ".equals(" + + override fun doRun(marker: IMarker, file: IFile) { + val offset = marker.getAttribute(IMarker.CHAR_START, 0) + val end = marker.getAttribute(IMarker.CHAR_END, 0) + val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) + val string = content.substring(offset, end) + val list = string.split(equalsName).filterNotNull() + if (list.size != 2) { + return + } + val replace = "${list[1].substringBeforeLast(')')}$equalsName${list[0]})" + val edit = ReplaceEdit(offset, string.length, replace) + val change = TextFileChange("", file) + change.edit = edit + change.perform(NullProgressMonitor()) + } + + override fun getLabel(): String { + return "Flip equals." + } + +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/SmartfoxActivator.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/SmartfoxActivator.kt new file mode 100644 index 0000000..046717e --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/SmartfoxActivator.kt @@ -0,0 +1,139 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse + +import com.alibaba.p3c.pmd.I18nResources +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RuleSetFactory +import net.sourceforge.pmd.RuleSets +import org.apache.log4j.Logger +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.Status +import org.eclipse.jface.dialogs.MessageDialog +import org.eclipse.jface.resource.ImageDescriptor +import org.eclipse.swt.graphics.Image +import org.eclipse.swt.widgets.Display +import org.eclipse.ui.plugin.AbstractUIPlugin +import org.osgi.framework.BundleContext + +/** + * @author caikang + * @date 2017/06/14 + */ +class SmartfoxActivator : AbstractUIPlugin() { + + init { + aDefault = this + } + + private val logger = Logger.getLogger(javaClass)!! + lateinit var ruleSets: RuleSets + private val localeKey = "p3c.locale" + + lateinit var ruleMap: Map + + @Throws(Exception::class) override fun start(context: BundleContext) { + super.start(context) + I18nResources.changeLanguage(locale) + ruleSets = createRuleSets() + ruleMap = ruleSets.allRules.associateBy { + it.name + } + } + + @Throws(Exception::class) override fun stop(context: BundleContext?) { + aDefault = null + super.stop(context) + } + + + fun getImage(key: String, iconPath: String = key): Image { + val registry = imageRegistry + var image: Image? = registry.get(key) + if (image == null) { + val descriptor = getImageDescriptor(iconPath) + registry.put(key, descriptor) + image = registry.get(key) + } + + return image!! + } + + val locale: String get() { + val lang = preferenceStore.getString(localeKey) + return if (lang.isNullOrBlank()) { + "zh" + } else { + lang + } + } + + fun toggleLocale() { + preferenceStore.setValue(localeKey, when (locale) { + "en" -> "zh" + else -> "en" + }) + } + + fun getRule(rule: String): Rule { + return ruleMap[rule]!! + } + + fun showError(message: String, t: Throwable) { + logError(message, t) + Display.getDefault().syncExec { + MessageDialog.openError(Display.getCurrent().activeShell, "错误", message + "\n" + t.toString()) + } + } + + fun logError(message: String, t: Throwable) { + log.log(Status(IStatus.ERROR, bundle.symbolicName, 0, message + t.message, t)) + logger.error(message, t) + } + + fun logError(status: IStatus) { + log.log(status) + logger.error(status.message, status.exception) + } + + fun logInformation(message: String) { + log.log(Status(IStatus.INFO, bundle.symbolicName, 0, message, null)) + } + + fun logWarn(message: String) { + log.log(Status(IStatus.WARNING, bundle.symbolicName, 0, message, null)) + } + + companion object { + // The plug-in ID + val PLUGIN_ID = "com.alibaba.smartfox.eclipse.plugin" + + var aDefault: SmartfoxActivator? = null + private set + + val instance: SmartfoxActivator get() = aDefault!! + + fun getImageDescriptor(path: String): ImageDescriptor { + return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, path) + } + + fun createRuleSets(): RuleSets { + val ruleSetFactory = RuleSetFactory() + val ruleSet = ruleSetFactory.createRuleSet("java-ali-pmd,vm-ali-other") + return RuleSets(ruleSet) + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/CodeAnalysisHandler.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/CodeAnalysisHandler.kt new file mode 100644 index 0000000..16108a6 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/CodeAnalysisHandler.kt @@ -0,0 +1,104 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.handler + +import com.alibaba.smartfox.eclipse.job.CodeAnalysis.processResources +import com.alibaba.smartfox.eclipse.message.P3cBundle +import com.google.common.collect.Sets +import org.apache.log4j.Logger +import org.eclipse.core.commands.AbstractHandler +import org.eclipse.core.commands.ExecutionEvent +import org.eclipse.core.commands.ExecutionException +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IResource +import org.eclipse.core.resources.IResourceVisitor +import org.eclipse.core.runtime.IAdaptable +import org.eclipse.jface.viewers.IStructuredSelection +import org.eclipse.ui.IFileEditorInput +import org.eclipse.ui.IWorkingSet +import org.eclipse.ui.commands.IElementUpdater +import org.eclipse.ui.handlers.HandlerUtil +import org.eclipse.ui.menus.UIElement +import org.eclipse.ui.part.EditorPart +import org.eclipse.ui.part.ViewPart + +/** + * @author caikang + * @date 2016/12/27 + */ +open class CodeAnalysisHandler : AbstractHandler(), IElementUpdater { + override fun updateElement(element: UIElement, parameters: MutableMap?) { + val text = P3cBundle.getMessage("com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler") + element.setText(text) + element.setTooltip(text) + } + + @Throws(ExecutionException::class) override fun execute(executionEvent: ExecutionEvent): Any? { + val selection = HandlerUtil.getCurrentSelectionChecked(executionEvent) + val part = HandlerUtil.getActivePart(executionEvent) + if (part is ViewPart) { + if (selection is IStructuredSelection) { + processForMutiFiles(selection) + } + } else if (part is EditorPart) { + val editorInput = HandlerUtil.getActiveEditorInput(executionEvent) + if (editorInput is IFileEditorInput) { + processResources(setOf(editorInput.file)) + } + } + return null + } + + private fun processForMutiFiles(selection: IStructuredSelection) { + val resources = getSelectionResources(selection) + processResources(resources) + } + + private fun getSelectionResources(selection: IStructuredSelection): MutableSet { + val resources = mutableSetOf() + selection.toList().forEach { + when (it) { + is IWorkingSet -> it.elements.mapTo(resources) { it.getAdapter(IResource::class.java) as IResource } + is IAdaptable -> { + val file = it.getAdapter(IResource::class.java) as? IResource ?: return@forEach + resources.add(file) + } + else -> log.warn("The selected object is not adaptable : ${it.toString()}") + } + } + return resources + } + + + companion object { + private val log = Logger.getLogger(CodeAnalysisHandler::class.java) + } +} + +class FileCollectVisitor : IResourceVisitor { + val fileSet = Sets.newLinkedHashSet()!! + + override fun visit(resource: IResource?): Boolean { + if (resource == null) { + return false + } + val file = resource.getAdapter(IFile::class.java) as? IFile ?: return true + if (file.exists() && (file.fileExtension == "java" || file.fileExtension == "vm")) { + fileSet.add(file) + } + return false + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/SwitchLanguageHandler.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/SwitchLanguageHandler.kt new file mode 100644 index 0000000..e12ba12 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/SwitchLanguageHandler.kt @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.handler + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.alibaba.smartfox.eclipse.message.P3cBundle +import org.eclipse.core.commands.AbstractHandler +import org.eclipse.core.commands.ExecutionEvent +import org.eclipse.jface.dialogs.MessageDialog +import org.eclipse.ui.PlatformUI +import org.eclipse.ui.commands.IElementUpdater +import org.eclipse.ui.menus.UIElement + +/** + * + * + * @author caikang + * @date 2017/06/21 + */ +class SwitchLanguageHandler : AbstractHandler(), IElementUpdater { + val handlerKey = "com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler" + val textKey = "$handlerKey.text.cur_" + + override fun execute(executionEvent: ExecutionEvent): Any? { + SmartfoxActivator.instance.toggleLocale() + if (!MessageDialog.openConfirm(null, "Tips", + P3cBundle.getMessage("$handlerKey.success.${SmartfoxActivator.instance.locale}"))) { + return null + } + PlatformUI.getWorkbench().restart() + return null + } + + override fun updateElement(element: UIElement, parameters: MutableMap?) { + val text = P3cBundle.getMessage("$textKey${SmartfoxActivator.instance.locale}") + element.setText(text) + element.setTooltip(text) + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/CodeAnalysis.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/CodeAnalysis.kt new file mode 100644 index 0000000..221f642 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/CodeAnalysis.kt @@ -0,0 +1,129 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.job + +import com.alibaba.p3c.pmd.lang.java.util.GeneratedCodeUtils +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler +import com.alibaba.smartfox.eclipse.handler.FileCollectVisitor +import com.alibaba.smartfox.eclipse.ui.InspectionResultView +import com.alibaba.smartfox.eclipse.ui.InspectionResults +import com.alibaba.smartfox.eclipse.ui.MarkerViolation +import com.alibaba.smartfox.eclipse.util.MarkerUtil +import com.google.common.io.Files +import net.sourceforge.pmd.PMDConfiguration +import net.sourceforge.pmd.PMDException +import net.sourceforge.pmd.Report +import net.sourceforge.pmd.RuleContext +import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.SourceCodeProcessor +import org.apache.log4j.Logger +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IResource +import org.eclipse.core.runtime.IProgressMonitor +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.SubMonitor +import org.eclipse.core.runtime.jobs.Job +import java.io.IOException +import java.io.StringReader +import java.nio.charset.Charset + +/** + * + * + * @author caikang + * @date 2017/06/14 + */ +object CodeAnalysis { + private val log = Logger.getLogger(CodeAnalysisHandler::class.java) + + fun processResources(resources: Set) { + InspectionResultView.activeViews() + val job = object : Job("P3C Code Analysis") { + override fun run(monitor: IProgressMonitor): IStatus { + val fileVisitor = FileCollectVisitor() + monitor.setTaskName("Collect files") + resources.forEach { + if (monitor.isCanceled) { + return@run Status.CANCEL_STATUS + } + if(it.isAccessible){ + it.accept(fileVisitor) + } + } + if (monitor.isCanceled) { + return Status.CANCEL_STATUS + } + val subMonitor = SubMonitor.convert(monitor, "Analysis files", fileVisitor.fileSet.size) + monitor.setTaskName("Analysis files") + fileVisitor.fileSet.forEach { iFile -> + if (monitor.isCanceled) { + return@run Status.CANCEL_STATUS + } + MarkerUtil.removeAllMarkers(iFile) + monitor.subTask(iFile.fullPath.toPortableString()) + val markers = processFileToMakers(iFile, monitor) + subMonitor.newChild(1) + InspectionResults.updateFileViolations(iFile, markers) + } + return Status.OK_STATUS + } + } + job.rule = P3CMutex + job.schedule() + } + + fun processFileToMakers(file: IFile, monitor: IProgressMonitor): List { + file.refreshLocal(IResource.DEPTH_ZERO, monitor) + val ruleViolations = processFile(file) + + MarkerUtil.removeAllMarkers(file) + val markers = ruleViolations.map { + MarkerViolation(MarkerUtil.addMarker(file, it), it) + } + return markers + } + + private fun processFile(file: IFile): List { + val configuration = PMDConfiguration() + configuration.sourceEncoding = file.charset ?: Charsets.UTF_8.name() + configuration.inputPaths = file.fullPath.toPortableString() + val ctx = RuleContext() + ctx.setAttribute("eclipseFile", file) + val niceFileName = configuration.inputPaths + val report = Report.createReport(ctx, niceFileName) + SmartfoxActivator.instance.ruleSets.start(ctx) + val processor = SourceCodeProcessor(configuration) + try { + ctx.languageVersion = null + val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) + if (!GeneratedCodeUtils.isGenerated(content)) { + processor.processSourceCode(StringReader(content), SmartfoxActivator.instance.ruleSets, ctx) + } + } catch (pmde: PMDException) { + log.debug("Error while processing file: " + niceFileName, pmde.cause) + report.addError(Report.ProcessingError(pmde.message, niceFileName)) + } catch (ioe: IOException) { + log.error("Unable to read source file: " + niceFileName, ioe) + } catch (re: RuntimeException) { + log.error("RuntimeException while processing file: " + niceFileName, re) + } finally { + SmartfoxActivator.instance.ruleSets.end(ctx) + } + return report.toList() + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/P3CMutex.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/P3CMutex.kt new file mode 100644 index 0000000..3f3b363 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/P3CMutex.kt @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.job + +import org.eclipse.core.resources.IResource +import org.eclipse.core.runtime.jobs.ISchedulingRule + +/** + * + * + * @author caikang + * @date 2017/06/14 + */ +object P3CMutex : ISchedulingRule { + override fun contains(rule: ISchedulingRule?): Boolean { + return isConflicting(rule) + } + + override fun isConflicting(rule: ISchedulingRule?): Boolean { + return rule == this || rule is IResource + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/message/P3cBundle.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/message/P3cBundle.kt new file mode 100644 index 0000000..1e302f0 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/message/P3cBundle.kt @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.message + + +import com.alibaba.p3c.pmd.I18nResources +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import java.util.Locale +import java.util.ResourceBundle + +/** + * + * + * @author caikang + * @date 2017/06/20 + */ +object P3cBundle { + private val resourceBundle = ResourceBundle.getBundle("messages.P3cBundle", + Locale(SmartfoxActivator.instance.locale), I18nResources.XmlControl()) + + fun getMessage(key: String): String { + return resourceBundle.getString(key) + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/RulePriority.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/RulePriority.kt new file mode 100644 index 0000000..260ffc1 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/RulePriority.kt @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd + +enum class RulePriority(val priority: Int, val title: String) { + + Blocker(1, "Blocker"), Critical(2, "Critical"), Major(3, "Major"); + + override fun toString(): String { + return title + } + + companion object { + fun valueOf(priority: Int): RulePriority { + try { + return RulePriority.values()[priority - 1] + } catch (e: ArrayIndexOutOfBoundsException) { + return Major + } + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AbstractEclipseRule.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AbstractEclipseRule.kt new file mode 100644 index 0000000..0b98fa5 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AbstractEclipseRule.kt @@ -0,0 +1,185 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd.rule + +import com.alibaba.smartfox.eclipse.message.P3cBundle +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RuleContext +import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule +import org.eclipse.core.resources.IFile +import org.eclipse.core.runtime.NullProgressMonitor +import org.eclipse.jdt.core.ICompilationUnit +import org.eclipse.jdt.core.IProblemRequestor +import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.core.JavaModelException +import org.eclipse.jdt.core.WorkingCopyOwner +import org.eclipse.jdt.core.compiler.IProblem +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.CompilationUnit +import java.util.MissingResourceException + +/** + * @author caikang + * @date 2016/12/26 + */ +abstract class AbstractEclipseRule : AbstractJavaRule() { + + open fun getErrorMessage(): String { + return message + } + + override fun visit(node: ASTCompilationUnit, data: Any): Any? { + val result = super.visit(node, data) + val ruleContext = data as RuleContext + val file = ruleContext.getAttribute("eclipseFile") as? IFile ?: return result + val compilationUnit = JavaCore.createCompilationUnitFrom(file) ?: return data + try { + val requestor = object : IProblemRequestor { + override fun acceptProblem(problem: IProblem) {} + + override fun beginReporting() {} + + override fun endReporting() {} + + override fun isActive(): Boolean { + return true + } + } + val workingCopy = compilationUnit.getWorkingCopy(null) + val ast = workingCopy.reconcile(JLS8, ICompilationUnit.FORCE_PROBLEM_DETECTION + or ICompilationUnit.ENABLE_BINDINGS_RECOVERY or ICompilationUnit.ENABLE_STATEMENTS_RECOVERY, + object : WorkingCopyOwner() { + override fun getProblemRequestor(workingCopy: ICompilationUnit?): IProblemRequestor { + return requestor + } + }, NullProgressMonitor()) ?: return data + ast.accept(getVisitor(ast, ruleContext)) + + } catch (e: JavaModelException) { + throw RuntimeException(e) + } + + return data + } + + override fun setDescription(description: String) { + try { + super.setDescription(P3cBundle.getMessage(description)) + } catch (e: MissingResourceException) { + super.setMessage(description) + } + + } + + override fun setMessage(message: String) { + try { + super.setMessage(P3cBundle.getMessage(message)) + } catch (e: MissingResourceException) { + super.setMessage(message) + } + + } + + + protected abstract fun getVisitor(ast: CompilationUnit, ruleContext: RuleContext): ASTVisitor + + internal fun addRuleViolation(ruleContext: RuleContext, ast: CompilationUnit, nodeInfo: NodeInfo) { + val rule = this + val ruleViolation = object : RuleViolation { + override fun getRule(): Rule { + return rule + } + + override fun getDescription(): String { + return getErrorMessage().trim { it <= ' ' } + } + + override fun isSuppressed(): Boolean { + return false + } + + override fun getFilename(): String { + return ruleContext.sourceCodeFilename + } + + override fun getBeginLine(): Int { + return ast.getLineNumber(nodeInfo.startPosition) + } + + override fun getBeginColumn(): Int { + return ast.getColumnNumber(nodeInfo.startPosition) + } + + override fun getEndLine(): Int { + return ast.getLineNumber(nodeInfo.endPosition) + } + + override fun getEndColumn(): Int { + return ast.getColumnNumber(nodeInfo.endPosition) + } + + override fun getPackageName(): String? { + return nodeInfo.packageName + } + + override fun getClassName(): String? { + return nodeInfo.className + } + + override fun getMethodName(): String? { + return nodeInfo.methodName + } + + override fun getVariableName(): String? { + return nodeInfo.variableName + } + + override fun toString(): String { + return rule.toString() + } + + + } + + ruleContext.report.addRuleViolation(ruleViolation) + } + + protected fun violation(rc: RuleContext, node: ASTNode, ast: CompilationUnit) { + val nodeInfo = NodeInfo() + nodeInfo.className = ast.javaElement.elementName + nodeInfo.packageName = ast.`package`.name.fullyQualifiedName + nodeInfo.startPosition = node.startPosition + nodeInfo.endPosition = node.startPosition + node.length + + addRuleViolation(rc, ast, nodeInfo) + } + + inner class NodeInfo { + internal var packageName: String? = null + internal var className: String? = null + internal var methodName: String? = null + internal var variableName: String? = null + internal var startPosition: Int = 0 + internal var endPosition: Int = 0 + } + + companion object { + val JLS8 = 8 + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidAccessStaticViaInstanceRule.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidAccessStaticViaInstanceRule.kt new file mode 100644 index 0000000..48ec34b --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidAccessStaticViaInstanceRule.kt @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd.rule + +import net.sourceforge.pmd.RuleContext +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.ClassInstanceCreation +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.FieldAccess +import org.eclipse.jdt.core.dom.IVariableBinding +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.Modifier +import org.eclipse.jdt.core.dom.QualifiedName +import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.VariableDeclarationFragment + +/** + * @author caikang + * @date 2016/12/27 + */ +class AvoidAccessStaticViaInstanceRule : AbstractEclipseRule() { + override fun getVisitor(ast: CompilationUnit, ruleContext: RuleContext): ASTVisitor { + return object : ASTVisitor() { + override fun visit(node: QualifiedName?): Boolean { + if (node!!.parent !is MethodInvocation && node.parent !is VariableDeclarationFragment) { + return false + } + val name = node.name + val binding = name.resolveBinding() + if (binding !is IVariableBinding || binding.getModifiers() and Modifier.STATIC == 0) { + return true + } + val qualifier = node.qualifier + val typeBinding = qualifier.resolveTypeBinding() + if (qualifier.isSimpleName && typeBinding.name != qualifier.fullyQualifiedName) { + violation(ruleContext, node, ast) + return false + } + if (qualifier.isQualifiedName) { + val qualifiedName = qualifier as QualifiedName + if (typeBinding.name != qualifiedName.name.identifier) { + violation(ruleContext, node, ast) + return false + } + } + return true + } + + override fun visit(node: FieldAccess): Boolean { + val variableBinding = node.resolveFieldBinding() ?: return false + if (variableBinding.modifiers and Modifier.STATIC == 0) { + return true + } + if (node.expression is ClassInstanceCreation) { + violation(ruleContext, node, ast) + return true + } + return true + } + + override fun visit(node: MethodInvocation?): Boolean { + val methodBinding = node?.resolveMethodBinding() ?: return false + if (methodBinding.modifiers and Modifier.STATIC == 0) { + return true + } + if (node.expression is ClassInstanceCreation) { + violation(ruleContext, node, ast) + return true + } + val expression = node.expression + if (expression is SimpleName && expression.identifier != expression.resolveTypeBinding().name) { + violation(ruleContext, node, ast) + return true + } + return true + } + } + } + +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidUseDeprecationRule.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidUseDeprecationRule.kt new file mode 100644 index 0000000..8bd9871 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidUseDeprecationRule.kt @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd.rule + +import net.sourceforge.pmd.RuleContext +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.ClassInstanceCreation +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.FieldAccess +import org.eclipse.jdt.core.dom.IVariableBinding +import org.eclipse.jdt.core.dom.ImportDeclaration +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.Modifier +import org.eclipse.jdt.core.dom.QualifiedName +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jdt.core.dom.VariableDeclarationFragment + +/** + * @author caikang + * @date 2016/12/27 + */ +class AvoidUseDeprecationRule : AbstractEclipseRule() { + override fun getVisitor(ast: CompilationUnit, ruleContext: RuleContext): ASTVisitor { + return object : ASTVisitor() { + override fun visit(node: ImportDeclaration?): Boolean { + if (node!!.resolveBinding() != null && node.resolveBinding().isDeprecated) { + violation(ruleContext, node, ast) + } + return true + } + + override fun visit(node: QualifiedName?): Boolean { + if (node!!.parent !is MethodInvocation && node.parent !is VariableDeclarationFragment) { + return false + } + val name = node.name + val binding = name.resolveBinding() + if (binding !is IVariableBinding || binding.getModifiers() and Modifier.STATIC == 0) { + return true + } + if (binding.isDeprecated()) { + violation(ruleContext, node, ast) + return false + } + val qualifier = node.qualifier + val typeBinding = qualifier.resolveTypeBinding() + if (typeBinding.isDeprecated) { + violation(ruleContext, node, ast) + return false + } + return true + } + + override fun visit(node: TypeDeclaration?): Boolean { + val superClass = node!!.superclassType + if (superClass != null && superClass.resolveBinding().isDeprecated) { + violation(ruleContext, node, ast) + return true + } + val interfaces = node.resolveBinding().interfaces + for (tb in interfaces) { + if (tb.isDeprecated) { + violation(ruleContext, node, ast) + return true + } + } + return true + } + + override fun visit(node: FieldAccess?): Boolean { + val variableBinding = node!!.resolveFieldBinding() ?: return false + if (variableBinding.isDeprecated) { + violation(ruleContext, node, ast) + } + return true + } + + override fun visit(node: MethodInvocation): Boolean { + val methodBinding = node.resolveMethodBinding() ?: return false + if (methodBinding.isDeprecated) { + violation(ruleContext, node, ast) + } + return true + } + + override fun visit(node: ClassInstanceCreation?): Boolean { + if (node!!.resolveTypeBinding().isDeprecated) { + violation(ruleContext, node, ast) + } + return true + } + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MapOrSetKeyShouldOverrideHashCodeEqualsRule.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MapOrSetKeyShouldOverrideHashCodeEqualsRule.kt new file mode 100644 index 0000000..7aafd4e --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MapOrSetKeyShouldOverrideHashCodeEqualsRule.kt @@ -0,0 +1,125 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd.rule + +import net.sourceforge.pmd.RuleContext +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.ITypeBinding +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.VariableDeclarationStatement + +/** + * @author zenghou.fw + * @date 2016/12/27 + */ +class MapOrSetKeyShouldOverrideHashCodeEqualsRule : AbstractEclipseRule() { + + val methodEquals = "equals" + val methodHashCode = "hashCode" + val methodAdd = "add" + val methodPut = "put" + + override fun getVisitor(ast: CompilationUnit, ruleContext: RuleContext): ASTVisitor { + return object : ASTVisitor() { + + override fun visit(node: VariableDeclarationStatement): Boolean { + if (!node.type.isParameterizedType) { + return true + } + val typeBinding = node.type.resolveBinding() + if (isSet(typeBinding) || isMap(typeBinding)) { + val argumentTypes = typeBinding.typeArguments + if (argumentTypes != null && argumentTypes.isNotEmpty()) { + if (!isOverrideEqualsAndHashCode(argumentTypes[0])) { + violation(ruleContext, node, ast) + return false + } + } + } + return true + } + + override fun visit(node: MethodInvocation): Boolean { + val methodBinding = node.resolveMethodBinding() ?: return false + val callerType = methodBinding.declaringClass + + if (methodAdd == methodBinding.name) { + if (!isSet(callerType)) { + return true + } + val parameterTypes = methodBinding.parameterTypes + if (parameterTypes != null && parameterTypes.isNotEmpty()) { + if (!isOverrideEqualsAndHashCode(parameterTypes[0])) { + violation(ruleContext, node, ast) + return false + } + } + return true + } + if (methodPut == methodBinding.name) { + if (!isMap(callerType)) { + return true + } + val parameterTypes = methodBinding.parameterTypes + if (parameterTypes != null && parameterTypes.isNotEmpty()) { + if (!isOverrideEqualsAndHashCode(parameterTypes[0])) { + violation(ruleContext, node, ast) + return false + } + } + } + return true + } + + private fun isOverrideEqualsAndHashCode(genericType: ITypeBinding): Boolean { + // skip enum + if (genericType.isEnum || genericType.isInterface || genericType.isTypeVariable || genericType.isWildcardType) { + return true + } + + val methodBindings = genericType.declaredMethods ?: return false + + val overrideCount = methodBindings.asSequence().filter { + //find equals(Object o) and hashCode() with @Override + methodEquals == it.name || methodHashCode == it.name + }.filter { + when (it.name) { + methodEquals -> { + val parameterTypes = it.parameterTypes + parameterTypes != null && parameterTypes.isNotEmpty() + && Object::class.java.name == parameterTypes[0].qualifiedName + } + methodHashCode -> { + val parameterTypes = it.parameterTypes + parameterTypes == null || parameterTypes.isEmpty() + } + else -> false + } + }.count() + return overrideCount == 2 + } + + private fun isSet(typeBinding: ITypeBinding): Boolean { + return java.util.Set::class.java.name == typeBinding.binaryName + } + + private fun isMap(typeBinding: ITypeBinding): Boolean { + return java.util.Map::class.java.name == typeBinding.binaryName + } + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MissingOverrideAnnotationRule.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MissingOverrideAnnotationRule.kt new file mode 100644 index 0000000..5007be9 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MissingOverrideAnnotationRule.kt @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.pmd.rule + +import com.alibaba.smartfox.eclipse.message.P3cBundle +import net.sourceforge.pmd.RuleContext +import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding +import java.util.Arrays +import javax.annotation.Generated + +/** + * @author caikang + * @date 2016/12/24 + */ +class MissingOverrideAnnotationRule : AbstractEclipseRule() { + override fun getErrorMessage(): String { + return P3cBundle.getMessage("rule.standalone.MissingOverrideAnnotationRule.error") + } + + override fun getVisitor(ast: CompilationUnit, ruleContext: RuleContext): ASTVisitor { + return MissingOverrideVisitor(ast, ruleContext) + } + + private inner class MissingOverrideVisitor(private val ast: CompilationUnit, + private val ruleContext: RuleContext) : ASTVisitor() { + + override fun visit(node: MethodDeclaration?): Boolean { + val methodBinding = node!!.resolveBinding() + val declaringClass = methodBinding.declaringClass ?: return super.visit(node) + if (declaringClass.isInterface) { + return super.visit(node) + } + val abs = methodBinding.annotations + if (abs.any { + Override::class.java.canonicalName == it.annotationType.binaryName + }) { + return super.visit(node) + } + try { + val field = methodBinding.javaClass.getDeclaredField("binding") + field.isAccessible = true + val internalBinding = field.get(methodBinding) as MethodBinding + if (internalBinding.isStatic || !(internalBinding.isImplementing || internalBinding.isOverriding) + || isGenerated(internalBinding)) { + return super.visit(node) + } + violation(ruleContext, node, ast) + } catch (e: Exception) { + e.printStackTrace() + } + + return super.visit(node) + } + + /** + * skip @Override check for generated code by lombok + * @param internalBinding + * @return + */ + private fun isGenerated(internalBinding: MethodBinding): Boolean { + val annotationBindings = internalBinding.annotations ?: return false + return annotationBindings.any { + it.annotationType != null && Arrays.equals(Generated::class.java.name.toCharArray(), + it.annotationType.readableName()) + } + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesPreferencePage.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesPreferencePage.kt new file mode 100644 index 0000000..c7acc8e --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesPreferencePage.kt @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import org.eclipse.jface.preference.PreferencePage +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Control +import org.eclipse.ui.IWorkbench +import org.eclipse.ui.IWorkbenchPreferencePage + +/** + * + * @author caikang + * @date 2017/09/01 + */ +class AllRulesPreferencePage : PreferencePage(), IWorkbenchPreferencePage { + override fun init(parent: IWorkbench?) { + } + + override fun createContents(parent: Composite): Control { + return AllRulesView(parent).content + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesView.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesView.kt new file mode 100644 index 0000000..b7578d4 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesView.kt @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import org.eclipse.swt.SWT +import org.eclipse.swt.layout.FillLayout +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.List + + +/** + * + * @author caikang + * @date 2017/09/01 + */ +class AllRulesView(parent: Composite) { + val content = Composite(parent, SWT.NONE) + + private val ruleList = SmartfoxActivator.instance.ruleSets.allRules.toList() + + init { + content.layout = FillLayout() + val list = List(content, SWT.BORDER or SWT.SINGLE or SWT.V_SCROLL or SWT.PUSH or SWT.H_SCROLL) + ruleList.forEach { + list.add(it.message) + } + val ruleDetail = RuleDetailComposite(content, SWT.PUSH) + list.addListener(SWT.Selection) { + val index = list.selectionIndex + val rule = ruleList.getOrNull(index) ?: return@addListener + ruleDetail.refresh(rule) + } + list.select(0) + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeContentProvider.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeContentProvider.kt new file mode 100644 index 0000000..26e541a --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeContentProvider.kt @@ -0,0 +1,68 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import org.eclipse.jface.viewers.ITreeContentProvider +import org.eclipse.jface.viewers.Viewer + +/** + * + * + * @author caikang + * @date 2017/06/08 + */ +object InspectionResultTreeContentProvider : ITreeContentProvider { + private lateinit var input: InspectionResults + + override fun getParent(element: Any?): Any { + return input + } + + override fun hasChildren(element: Any?): Boolean { + return element is InspectionResults || element is LevelViolations || element is RuleViolations + || element is FileMarkers + } + + override fun getChildren(parentElement: Any?): Array { + if (parentElement is InspectionResults) { + return parentElement.errors.toTypedArray() + } + if (parentElement is LevelViolations) { + return parentElement.rules.toTypedArray() + } + if (parentElement is RuleViolations) { + return parentElement.files.toTypedArray() + } + if (parentElement is FileMarkers) { + return parentElement.markers.toTypedArray() + } + return emptyArray() + } + + override fun getElements(inputElement: Any?): Array { + return input.errors.toTypedArray() + } + + override fun inputChanged(viewer: Viewer?, oldInput: Any?, newInput: Any?) { + if (newInput == null) { + return + } + this.input = newInput as InspectionResults + } + + override fun dispose() { + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeLabelProvider.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeLabelProvider.kt new file mode 100644 index 0000000..286ce0a --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeLabelProvider.kt @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.alibaba.smartfox.eclipse.pmd.RulePriority +import org.eclipse.jface.viewers.LabelProvider +import org.eclipse.swt.graphics.Image + +/** + * + * + * @author caikang + * @date 2017/06/08 + */ +object InspectionResultTreeLabelProvider : LabelProvider() { + override fun getImage(element: Any?): Image? { + if (element is LevelViolations) { + val imageName = when (element.level) { + RulePriority.Blocker.title, RulePriority.Critical.title -> "${element.level}.gif".toLowerCase() + else -> "${element.level}.png".toLowerCase() + } + return SmartfoxActivator.instance.getImage("icons/view/$imageName") + } + if (element is FileMarkers) { + if (element.file.fullPath.toPortableString().endsWith("java")) { + return SmartfoxActivator.instance.getImage("icons/view/class_obj.png") + } + return SmartfoxActivator.instance.getImage("icons/view/file_obj.png") + } + return null + } + + override fun getText(element: Any?): String { + if (element is LevelViolations) { + return "${element.level} (${element.count} Violations)" + } + if (element is RuleViolations) { + val rule = SmartfoxActivator.instance.getRule(element.rule) + return "${rule.message} (${element.count} Violations)" + } + if (element is FileMarkers) { + return element.file.fullPath.toPortableString().substringAfterLast("/") + + " (${element.markers.size} Violations)" + } + if (element is MarkerViolation) { + val desc = element.violation.description + return "$desc (at line ${element.violation.beginLine})" + } + return element.toString() + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultView.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultView.kt new file mode 100644 index 0000000..0002521 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultView.kt @@ -0,0 +1,249 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.alibaba.smartfox.eclipse.job.P3CMutex +import com.alibaba.smartfox.eclipse.util.MarkerUtil +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IMarker +import org.eclipse.core.runtime.IProgressMonitor +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.jobs.Job +import org.eclipse.jface.action.Action +import org.eclipse.jface.action.Separator +import org.eclipse.jface.util.OpenStrategy +import org.eclipse.jface.viewers.ISelection +import org.eclipse.jface.viewers.IStructuredSelection +import org.eclipse.jface.viewers.ITreeSelection +import org.eclipse.jface.viewers.TreeViewer +import org.eclipse.swt.SWT +import org.eclipse.swt.layout.FillLayout +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Display +import org.eclipse.ui.IWorkbenchPage +import org.eclipse.ui.OpenAndLinkWithEditorHelper +import org.eclipse.ui.PartInitException +import org.eclipse.ui.PlatformUI +import org.eclipse.ui.ide.IDE +import org.eclipse.ui.ide.ResourceUtil +import org.eclipse.ui.internal.views.markers.MarkerSupportInternalUtilities +import org.eclipse.ui.part.ViewPart +import org.eclipse.ui.texteditor.ITextEditor +import java.util.HashSet + +/** + * + * + * @author caikang + * @date 2017/06/08 + */ +class InspectionResultView : ViewPart() { + lateinit var treeViewer: TreeViewer + + private val quickFixAction = QuickFixAction(this) + + override fun setFocus() { + treeViewer.control.setFocus() + } + + override fun createPartControl(parent: Composite) { + parent.layout = FillLayout() + treeViewer = TreeViewer(parent, SWT.MULTI or SWT.H_SCROLL or SWT.V_SCROLL) + treeViewer.setUseHashlookup(true) + treeViewer.contentProvider = InspectionResultTreeContentProvider + treeViewer.labelProvider = InspectionResultTreeLabelProvider + treeViewer.input = InspectionResults + + InspectionResults.view = this + + addDoubleClickListener() + + addSelectionChangedListener() + + initToolBar() + + addLinkWithEditorSupport() + site.selectionProvider = treeViewer + } + + fun clear() { + InspectionResults.clear() + refreshView(InspectionResults) + } + + fun refreshView(input: InspectionResults) { + Display.getDefault().asyncExec { + treeViewer.refresh(input, true) + contentDescription = input.contentDescription + } + } + + private fun addSelectionChangedListener() { + treeViewer.addSelectionChangedListener inner@ { + val selection = it.selection as? IStructuredSelection ?: return@inner + val item = selection.firstElement ?: return@inner + val ruleDetailView = RuleDetailView.showAndGetView() + when (item) { + is MarkerViolation -> { + ruleDetailView.refresh(item.violation.rule) + quickFixAction.updateFileMarkers(listOf(FileMarkers(item.marker.resource as IFile, listOf(item)))) + } + is FileMarkers -> { + ruleDetailView.refresh(item.markers.first().violation.rule) + quickFixAction.updateFileMarkers(listOf(item)) + } + is RuleViolations -> { + ruleDetailView.refresh(SmartfoxActivator.instance.getRule(item.rule)) + quickFixAction.updateFileMarkers(item.files) + } + else -> { + quickFixAction.updateFileMarkers(emptyList()) + } + } + } + } + + private fun initToolBar() { + val bars = viewSite.actionBars + val tm = bars.toolBarManager + + val clearAction = object : Action("Clear Markers") { + override fun run() { + val job = object : Job("Clear Markers") { + override fun run(monitor: IProgressMonitor): IStatus { + if (monitor.isCanceled) { + Status.CANCEL_STATUS + } + clear() + return Status.OK_STATUS + } + } + job.rule = P3CMutex + job.schedule() + } + } + + clearAction.imageDescriptor = SmartfoxActivator.getImageDescriptor("icons/actions/clear.png") + + tm.add(Separator("Markers")) + tm.add(clearAction) + + tm.add(Separator("FilterGroup")) + tm.add(quickFixAction) + } + + private fun addLinkWithEditorSupport() { + object : OpenAndLinkWithEditorHelper(treeViewer) { + override fun activate(selection: ISelection) { + val currentMode = OpenStrategy.getOpenMethod() + try { + OpenStrategy.setOpenMethod(OpenStrategy.DOUBLE_CLICK) + openSelectedMarkers() + } finally { + OpenStrategy.setOpenMethod(currentMode) + } + } + + override fun linkToEditor(selection: ISelection?) { + } + + override fun open(selection: ISelection, activate: Boolean) { + val structured = selection as ITreeSelection + val element = structured.firstElement as? MarkerViolation ?: return + val page = site.page + if (element.marker.exists()) { + openMarkerInEditor(element.marker, page) + return + } + val file = element.marker.resource as IFile + val editor = IDE.openEditor(page, file) as ITextEditor + editor.selectAndReveal(MarkerUtil.getAbsoluteRange(file, element.violation).start, 0) + } + } + } + + internal fun openSelectedMarkers() { + val markers = getOpenableMarkers() + for (marker in markers) { + val page = site.page + openMarkerInEditor(marker, page) + } + } + + private fun getOpenableMarkers(): Array { + val structured = treeViewer.selection as ITreeSelection + val elements = structured.iterator() + val result = HashSet() + + while (elements.hasNext()) { + val marker = elements.next() as? IMarker ?: return emptyArray() + result.add(marker) + } + return result.toTypedArray() + } + + + fun openMarkerInEditor(marker: IMarker?, page: IWorkbenchPage) { + val editor = page.activeEditor + if (editor != null) { + val input = editor.editorInput + val file = ResourceUtil.getFile(input) + if (file != null) { + if (marker!!.resource == file && OpenStrategy.activateOnOpen()) { + page.activate(editor) + } + } + } + + if (marker != null && marker.resource is IFile) { + try { + IDE.openEditor(page, marker, OpenStrategy.activateOnOpen()) + } catch (e: PartInitException) { + MarkerSupportInternalUtilities.showViewError(e) + } + + } + } + + private fun addDoubleClickListener() { + treeViewer.addDoubleClickListener({ event -> + val selection = event.selection + if (selection !is ITreeSelection || selection.size() != 1) { + return@addDoubleClickListener + } + val obj = selection.firstElement + if (treeViewer.isExpandable(obj)) { + treeViewer.setExpandedState(obj, !treeViewer.getExpandedState(obj)) + } + }) + } + + companion object { + val viewId = "com.alibaba.smartfox.eclipse.ui.InspectionResultView" + + fun activeViews() { + PlatformUI.getWorkbench().activeWorkbenchWindow.activePage.showView(viewId) + RuleDetailView.showAndGetView() + } + + fun getView(): InspectionResultView { + return PlatformUI.getWorkbench().activeWorkbenchWindow.activePage.findView(viewId) as InspectionResultView + } + } +} + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResults.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResults.kt new file mode 100644 index 0000000..5972121 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResults.kt @@ -0,0 +1,102 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.pmd.RulePriority +import com.alibaba.smartfox.eclipse.util.MarkerUtil +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IMarker + +/** + * + * + * @author caikang + * @date 2017/06/13 + */ +object InspectionResults { + val fileViolations = linkedMapOf>() + + var contentDescription = "" + + val errors: List get() = run { + val result = toLevelViolationList(fileViolations.values.flatten()) + contentDescription = getContentDescription(result) + result + } + + lateinit var view: InspectionResultView + + + fun clear() { + fileViolations.forEach { t, u -> + MarkerUtil.removeAllMarkers(t) + } + fileViolations.clear() + // update contentDescription + errors + } + + private fun toLevelViolationList(markers: Collection): List { + return markers.groupBy { + it.violation.rule.priority.priority + }.mapValues { + it.value.groupBy { + it.violation.rule.name + }.mapValues { + it.value.groupBy { + it.marker.resource as IFile + }.map { + FileMarkers(it.key, it.value) + } + }.map { + RuleViolations(it.key, it.value) + } + }.toSortedMap().map { + val level = RulePriority.valueOf(it.key).title + LevelViolations(level, it.value) + } + } + + fun updateFileViolations(file: IFile, markers: List) { + if (markers.isEmpty()) { + fileViolations.remove(file) + } else { + fileViolations[file] = markers + } + view.refreshView(this) + } + + fun removeMarker(marker: IMarker) { + val file = marker.resource as IFile + val list = fileViolations[file] ?: return + val result = list.filter { + it.marker != marker + } + fileViolations[file] = result + marker.delete() + view.refreshView(this) + } + + fun getContentDescription(errors: List): String { + val map = errors.associateBy { + it.level + } + + return "${map[RulePriority.Blocker.title]?.count ?: 0} Blockers," + + "${map[RulePriority.Critical.title]?.count ?: 0} Criticals," + + "${map[RulePriority.Major.title]?.count ?: 0} Majors" + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/QuickFixAction.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/QuickFixAction.kt new file mode 100644 index 0000000..534237e --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/QuickFixAction.kt @@ -0,0 +1,108 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.p3c.pmd.lang.java.rule.flowcontrol.NeedBraceRule +import com.alibaba.smartfox.eclipse.RunWithoutViewRefresh +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.alibaba.smartfox.eclipse.job.CodeAnalysis +import com.alibaba.smartfox.eclipse.job.P3CMutex +import com.alibaba.smartfox.eclipse.pmd.rule.MissingOverrideAnnotationRule +import com.alibaba.smartfox.eclipse.util.CleanUps +import com.alibaba.smartfox.eclipse.util.getResolution +import org.eclipse.core.runtime.IProgressMonitor +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.SubMonitor +import org.eclipse.core.runtime.jobs.Job +import org.eclipse.jface.action.Action + +/** + * + * + * @author caikang + * @date 2017/06/14 + */ +class QuickFixAction(val view: InspectionResultView) : Action("Quick Fix") { + init { + imageDescriptor = SmartfoxActivator.getImageDescriptor("icons/actions/quickfixBulb.png") + isEnabled = false + } + + var markers = listOf() + + fun updateFileMarkers(markers: List) { + this.markers = markers + isEnabled = enabled() + } + + override fun run() { + if (markers.isEmpty()) { + return + } + runJob() + } + + private fun runJob() { + val job = object : Job("Perform Quick Fix") { + override fun run(monitor: IProgressMonitor): IStatus { + val subMonitor = SubMonitor.convert(monitor, markers.size) + monitor.setTaskName("Process File") + markers.forEach { + if (monitor.isCanceled) { + return@run Status.CANCEL_STATUS + } + monitor.subTask(it.file.name) + val childMonitor = subMonitor.newChild(1) + if (useCleanUpRefactoring()) { + CleanUps.fix(it.file, childMonitor) + } else { + it.markers.filter { it.marker.exists() }.forEach { + (it.marker.getResolution() as RunWithoutViewRefresh).run(it.marker, true) + } + } + val markers = CodeAnalysis.processFileToMakers(it.file, monitor) + InspectionResults.updateFileViolations(it.file, markers) + } + + return Status.OK_STATUS + } + } + job.rule = P3CMutex + job.schedule() + + } + + fun enabled(): Boolean { + if (useCleanUpRefactoring()) { + return true + } + if (markers.isEmpty()) { + return false + } + val marker = markers.first().markers.first().marker + return marker.exists() && marker.getResolution() != null + } + + private fun useCleanUpRefactoring(): Boolean { + if (markers.isEmpty()) { + return false + } + val ruleName = markers.first().markers.first().violation.rule.name + return ruleName == MissingOverrideAnnotationRule::class.java.simpleName + || ruleName == NeedBraceRule::class.java.simpleName + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailComposite.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailComposite.kt new file mode 100644 index 0000000..667e276 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailComposite.kt @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.pmd.RulePriority +import com.alibaba.smartfox.eclipse.ui.pmd.ContentBuilder +import com.alibaba.smartfox.eclipse.ui.pmd.StringArranger +import net.sourceforge.pmd.Rule +import org.eclipse.swt.SWT +import org.eclipse.swt.custom.StyledText +import org.eclipse.swt.layout.FillLayout +import org.eclipse.swt.widgets.Composite + +/** + * + * @author caikang + * @date 2017/09/01 + */ +class RuleDetailComposite(parent: Composite, style: Int = SWT.NONE) { + private val viewField: StyledText + private val contentBuilder = ContentBuilder() + private val arranger = StringArranger(" ") + private val panel = Composite(parent, style) + + init { + panel.layout = FillLayout() + + viewField = StyledText(panel, SWT.BORDER or SWT.H_SCROLL or SWT.V_SCROLL) + viewField.wordWrap = true + viewField.tabs = 20 + viewField.text = "Select a result item to show rule detail." + viewField.editable = false + } + + fun refresh(rule: Rule) { + contentBuilder.clear() + viewField.text = "" + contentBuilder.addHeading("Name") + contentBuilder.addText(rule.name) + contentBuilder.addHeading("Severity") + contentBuilder.addText(RulePriority.valueOf(rule.priority.priority).title) + contentBuilder.addHeading("Message") + contentBuilder.addText(rule.message) + if (!rule.description.isNullOrBlank()) { + contentBuilder.addHeading("Description") + contentBuilder.addRawText(arranger.format(rule.description).toString()) + } + + val examples = rule.examples + if (examples.isEmpty()) { + contentBuilder.showOn(viewField) + return + } + + contentBuilder.setLanguage(rule.language) + + contentBuilder.addHeading("Examples") + contentBuilder.addText("") + for (example in rule.examples) { + contentBuilder.addCode(example.trim { it <= ' ' }) + contentBuilder.addText("") + } + contentBuilder.showOn(viewField) + + if (contentBuilder.hasLinks()) { + contentBuilder.addLinkHandler(viewField) + } + viewField.update() + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailView.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailView.kt new file mode 100644 index 0000000..915adf7 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailView.kt @@ -0,0 +1,92 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import net.sourceforge.pmd.Rule +import org.eclipse.jface.action.Action +import org.eclipse.jface.action.Separator +import org.eclipse.swt.SWT +import org.eclipse.swt.custom.ScrolledComposite +import org.eclipse.swt.layout.FillLayout +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Display +import org.eclipse.swt.widgets.Shell +import org.eclipse.ui.IWorkbenchPage +import org.eclipse.ui.PlatformUI +import org.eclipse.ui.part.ViewPart + + +/** + * + * + * @author caikang + * @date 2017/06/12 + */ +open class RuleDetailView : ViewPart() { + + private lateinit var ruleDetailComposite: RuleDetailComposite + + override fun setFocus() { + } + + override fun createPartControl(parent: Composite) { + ruleDetailComposite = RuleDetailComposite(parent) + initToolBar() + } + + fun refresh(rule: Rule) { + ruleDetailComposite.refresh(rule) + } + + + private fun initToolBar() { + val bars = viewSite.actionBars + val tm = bars.toolBarManager + + val rulesAction = object : Action("Show Rules") { + override fun run() { + val shell = Shell(Display.getDefault()) + shell.text = "All Rules" + shell.layout = FillLayout() + + val sc = ScrolledComposite(shell, SWT.V_SCROLL or SWT.H_SCROLL) + + val rulesView = AllRulesView(sc) + + sc.expandHorizontal = true + sc.expandVertical = true + + sc.content = rulesView.content + + shell.open() + } + } + + rulesAction.imageDescriptor = SmartfoxActivator.getImageDescriptor("icons/actions/rules.png") + + tm.add(Separator("Markers")) + tm.add(rulesAction) + } + + companion object { + fun showAndGetView(): RuleDetailView { + return PlatformUI.getWorkbench().activeWorkbenchWindow.activePage.showView( + "com.alibaba.smartfox.eclipse.ui.RuleDetailView", null, + IWorkbenchPage.VIEW_VISIBLE) as RuleDetailView + } + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/Violations.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/Violations.kt new file mode 100644 index 0000000..b5aee0d --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/Violations.kt @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.eclipse.ui + +import com.alibaba.smartfox.eclipse.util.MarkerUtil +import net.sourceforge.pmd.RuleViolation +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IMarker + +/** + * + * + * @author caikang + * @date 2017/06/13 + */ +data class LevelViolations(var level: String, var rules: List, + var count: Int = rules.sumBy { it.count }) { + fun removeMarkers() { + rules.forEach { + it.removeMarkers() + } + } +} + +data class RuleViolations(var rule: String, var files: List, + var count: Int = files.sumBy { it.markers.size }) { + fun removeMarkers() { + files.forEach { + it.removeMarkers() + } + } +} + +data class FileMarkers(var file: IFile, var markers: List) { + fun removeMarkers() { + MarkerUtil.removeAllMarkers(file) + } +} + +data class MarkerViolation(val marker: IMarker, val violation: RuleViolation) \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/BasicLineStyleListener.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/BasicLineStyleListener.kt new file mode 100644 index 0000000..aff3777 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/BasicLineStyleListener.kt @@ -0,0 +1,20 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import org.eclipse.swt.custom.LineStyleEvent +import org.eclipse.swt.custom.LineStyleListener + +/** + * This class performs the syntax highlighting and styling for Pmpe + * * PmpeLineStyleListener constructor + + * @param theSyntaxData the syntax data to use + */ +class BasicLineStyleListener(theSyntaxData: SyntaxData) : StyleExtractor(theSyntaxData), LineStyleListener { + /** + * Called by StyledText to get styles for a line + */ + override fun lineGetStyle(event: LineStyleEvent) { + val styles = lineStylesFor(event.lineText, event.lineOffset, event.lineText.length) + event.styles = styles.toTypedArray() + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/ContentBuilder.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/ContentBuilder.kt new file mode 100644 index 0000000..21ca572 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/ContentBuilder.kt @@ -0,0 +1,158 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import net.sourceforge.pmd.lang.Language +import org.eclipse.swt.SWT +import org.eclipse.swt.custom.StyleRange +import org.eclipse.swt.custom.StyledText +import org.eclipse.swt.graphics.Point +import org.eclipse.swt.widgets.Display +import org.eclipse.ui.PlatformUI +import java.net.URL +import java.util.ArrayList +import java.util.Arrays +import java.util.HashMap + +/** + * + * @author caikang + * @date 2017/07/20 + */ +class ContentBuilder { + private val CR = '\n' + private val buffer = StringBuilder() + private val headingSpans = ArrayList() + private val codeSpans = ArrayList() + private val linksBySpan = HashMap() + + private val indentDepth: Int = 3 + + private val codeStyleExtractor = StyleExtractor(SyntaxManager.getSyntaxData("java")) + + private val background = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE) + + private val headingColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE) + + private val codeStyle = FontBuilder("Courier", 11, SWT.NORMAL).style(Display.getCurrent()) + + fun clear() { + buffer.setLength(0) + headingSpans.clear() + codeSpans.clear() + linksBySpan.clear() + } + + fun addHeading(heading: String) { + var length = buffer.length + if (length > 0) { + buffer.append(CR) + length += 1 + } + + headingSpans.add(intArrayOf(length, length + heading.length)) + buffer.append(heading).append(CR) + } + + fun addText(text: String) { + for (i in 0 until indentDepth) { + buffer.append(' ') + } + buffer.append(text).append(CR) + } + + fun addRawText(text: String) { + buffer.append(text) + } + + fun addCode(code: String) { + val length = buffer.length + codeSpans.add(intArrayOf(length, length + code.length)) + buffer.append(code) + } + + fun setLanguage(language: Language) { + val syntax = SyntaxManager.getSyntaxData(language.terseName) + codeStyleExtractor.syntax(syntax) + } + + fun hasLinks(): Boolean { + return linksBySpan.isNotEmpty() + } + + fun addLinkHandler(widget: StyledText) { + + widget.addListener(SWT.MouseDown) { event -> + // It is up to the application to determine when and how a link + // should be activated. + // In this snippet links are activated on mouse down when the + // control key is held down + // if ((event.stateMask & SWT.MOD1) != 0) { + try { + val offset = widget.getOffsetAtLocation(Point(event.x, event.y)) + val link = linkAt(offset) + if (link != null) { + launchBrowser(link) + } + + } catch (e: IllegalArgumentException) { + // no character under event.x, event.y + } + } + } + + private fun linkAt(textIndex: Int): String? { + var span: IntArray + for ((key, value) in linksBySpan) { + span = key + if (span[0] <= textIndex && textIndex <= span[1]) { + return value + } + } + return null + } + + private fun launchBrowser(link: String) { + try { + val browser = PlatformUI.getWorkbench().browserSupport.externalBrowser + browser.openURL(URL(link)) + } catch (ex: Exception) { + ex.printStackTrace() + } + } + + fun showOn(widget: StyledText) { + val text = buffer.toString() + widget.text = text + val ranges = ArrayList() + var span: IntArray + for (i in headingSpans.indices) { + span = headingSpans[i] + ranges.add(StyleRange(span[0], span[1] - span[0], headingColor, background, SWT.BOLD)) + } + for (spn in linksBySpan.keys) { + val style = StyleRange(spn[0], spn[1] - spn[0], headingColor, background, SWT.UNDERLINE_LINK) + style.underline = true + ranges.add(style) + } + val crStr = Character.toString(CR) + var sr: StyleRange + + for (i in codeSpans.indices) { + span = codeSpans[i] + sr = StyleRange(codeStyle) + sr.start = span[0] + sr.length = span[1] - span[0] + + val colorRanges = codeStyleExtractor.stylesFor(text, sr.start, sr.length, crStr) + ranges += colorRanges + } + // must be in order! + val styles = sort(ranges) + widget.styleRanges = styles + } + + private fun sort(ranges: List): Array { + val styles = ranges.toTypedArray() + Arrays.sort(styles) { sr1, sr2 -> sr1.start - sr2.start } + return styles + } +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/FontBuilder.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/FontBuilder.kt new file mode 100644 index 0000000..75b6135 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/FontBuilder.kt @@ -0,0 +1,19 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import org.eclipse.swt.graphics.Font +import org.eclipse.swt.graphics.TextStyle +import org.eclipse.swt.widgets.Display + +/** + * @author Brian Remedios + */ +class FontBuilder(val name: String, val size: Int, val style: Int, val colorIdx: Int = -1) { + + fun build(display: Display): Font { + return Font(display, name, size, style) + } + + fun style(display: Display): TextStyle { + return TextStyle(build(display), if (colorIdx < 0) null else display.getSystemColor(colorIdx), null) + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StringArranger.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StringArranger.kt new file mode 100644 index 0000000..78cd5ae --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StringArranger.kt @@ -0,0 +1,54 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import net.sourceforge.pmd.util.StringUtil +import java.util.ArrayList + +/** + * @author Brian Remedios + */ +class StringArranger(private val indentString: String) { + + fun withIndent(rawText: String): String { + return indentString + rawText + } + + fun format(rawText: String): StringBuilder { + + val sb = StringBuilder() + for (line in trimmedLinesIn(rawText)) { + sb.append(indentString) + sb.append(line).append(CR) + } + + return sb + } + + fun trimmedLinesIn(text: String): List { + + val lines = text.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (lines.isEmpty()) { + return emptyList() + } + + val lineSet = ArrayList(lines.size) + + var startLine = 0 + while (startLine < lines.size && StringUtil.isEmpty(lines[startLine])) { + startLine++ + } + + var endLine = lines.size - 1 + while (endLine >= 0 && StringUtil.isEmpty(lines[endLine])) { + endLine-- + } + + lines.mapTo(lineSet) { + it.trim { it <= ' ' } + } + return lineSet + } + + companion object { + private val CR = '\n' + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StyleExtractor.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StyleExtractor.kt new file mode 100644 index 0000000..07b8821 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StyleExtractor.kt @@ -0,0 +1,275 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import net.sourceforge.pmd.util.StringUtil +import org.eclipse.swt.SWT +import org.eclipse.swt.custom.StyleRange +import org.eclipse.swt.widgets.Display +import java.util.ArrayList +import java.util.LinkedList + +/** + * @author Brian Remedios + */ +open class StyleExtractor(private var syntaxData: SyntaxData?) { + private val commentOffsets: MutableList + + init { + commentOffsets = LinkedList() + } + + fun syntax(theSyntax: SyntaxData?) { + syntaxData = theSyntax + } + + /** + * Refreshes the offsets for all multiline comments in the parent + * StyledText. The parent StyledText should call this whenever its text is + * modified. Note that this code doesn't ignore comment markers inside + * strings. + + * @param text the text from the StyledText + */ + fun refreshMultilineComments(text: String) { + // Clear any stored offsets + commentOffsets.clear() + + if (syntaxData != null) { + // Go through all the instances of COMMENT_START + var pos = text.indexOf(syntaxData!!.multiLineCommentStart!!) + while (pos > -1) { + // offsets[0] holds the COMMENT_START offset + // and COMMENT_END holds the ending offset + val offsets = IntArray(2) + offsets[0] = pos + + // Find the corresponding end comment. + pos = text.indexOf(syntaxData!!.multiLineCommentEnd!!, pos) + + // If no corresponding end comment, use the end of the text + offsets[1] = if (pos == -1) text.length - 1 else pos + syntaxData!!.multiLineCommentEnd!!.length - 1 + pos = offsets[1] + // Add the offsets to the collection + commentOffsets.add(offsets) + pos = text.indexOf(syntaxData!!.multiLineCommentStart!!, pos) + } + } + } + + /** + * Checks to see if the specified section of text begins inside a multiline + * comment. Returns the index of the closing comment, or the end of the line + * if the whole line is inside the comment. Returns -1 if the line doesn't + * begin inside a comment. + + * @param start the starting offset of the text + * @param length the length of the text + * @return int + */ + private fun getBeginsInsideComment(start: Int, length: Int): Int { + // Assume section doesn't being inside a comment + var index = -1 + + // Go through the multiline comment ranges + var i = 0 + val n = commentOffsets.size + while (i < n) { + val offsets = commentOffsets[i] + + // If starting offset is past range, quit + if (offsets[0] > start + length) { + break + } + // Check to see if section begins inside a comment + if (offsets[0] <= start && offsets[1] >= start) { + // It does; determine if the closing comment marker is inside + // this section + index = if (offsets[1] > start + length) start + length + else offsets[1] + syntaxData!!.multiLineCommentEnd!!.length - 1 + } + i++ + } + return index + } + + private fun isDefinedVariable(text: String): Boolean { + return StringUtil.isNotEmpty(text) + } + + private fun atMultiLineCommentStart(text: String, position: Int): Boolean { + return text.indexOf(syntaxData!!.multiLineCommentStart!!, position) == position + } + + private fun atStringStart(text: String, position: Int): Boolean { + return text.indexOf(syntaxData!!.stringStart!!, position) == position + } + + private fun atVarnameReference(text: String, position: Int): Boolean { + return syntaxData!!.varnameReference != null && text.indexOf(syntaxData!!.varnameReference!!, + position) == position + } + + private fun atSingleLineComment(text: String, position: Int): Boolean { + return syntaxData!!.comment != null && text.indexOf(syntaxData!!.comment!!, position) == position + } + + private fun getKeywordEnd(lineText: String, start: Int): Int { + + val length = lineText.length + + val buf = StringBuilder(length) + var i = start + + // Call any consecutive letters a word + while (i < length && Character.isLetter(lineText[i])) { + buf.append(lineText[i]) + i++ + } + + return if (syntaxData!!.isKeyword(buf.toString())) i else 0 - i + } + + /** + * Chop up the text into individual lines starting from offset and then + * determine the required styles for each. Ensures the offset is properly + * accounted for in each. + + * @param text + * @param offset + * @param length + * @return + */ + fun stylesFor(text: String, offset: Int, length: Int, lineSeparator: String): List { + + if (syntaxData == null) { + return emptyList() + } + + val content = text.substring(offset, offset + length) + val lines = content.split(lineSeparator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + + val styles = ArrayList() + + val separatorLength = lineSeparator.length + + var currentOffset = offset + + for (line in lines) { + val lineLength = line.length + val lineStyles = lineStylesFor(line, 0, lineLength) + + for (sr in lineStyles) { + sr.start += currentOffset + } + styles.addAll(lineStyles) + currentOffset += lineLength + separatorLength + } + + return styles + } + + fun lineStylesFor(lineText: String, lineOffset: Int, length: Int): List { + + val styles = ArrayList() + + var start = 0 + + // Check if line begins inside a multiline comment + val mlIndex = getBeginsInsideComment(lineOffset, lineText.length) + if (mlIndex > -1) { + // Line begins inside multiline comment; create the range + styles.add(StyleRange(lineOffset, mlIndex - lineOffset, COMMENT_COLOR, COMMENT_BACKGROUND)) + start = mlIndex + } + // Do punctuation, single-line comments, and keywords + while (start < length) { + // Check for multiline comments that begin inside this line + if (atMultiLineCommentStart(lineText, start)) { + // Determine where comment ends + var endComment = lineText.indexOf(syntaxData!!.multiLineCommentEnd!!, start) + + // If comment doesn't end on this line, extend range to end of + // line + if (endComment == -1) { + endComment = length + } else { + endComment += syntaxData!!.multiLineCommentEnd!!.length + } + styles.add(StyleRange(lineOffset + start, endComment - start, COMMENT_COLOR, COMMENT_BACKGROUND)) + + start = endComment + } else if (atStringStart(lineText, start)) { + // Determine where comment ends + var endString = lineText.indexOf(syntaxData!!.stringEnd!!, start + 1) + + // If string doesn't end on this line, extend range to end of + // line + if (endString == -1) { + endString = length + } else { + endString += syntaxData!!.stringEnd!!.length + } + styles.add(StyleRange(lineOffset + start, endString - start, STRING_COLOR, COMMENT_BACKGROUND)) + + start = endString + } else if (atSingleLineComment(lineText, start)) { + // line comments + + styles.add(StyleRange(lineOffset + start, length - start, COMMENT_COLOR, COMMENT_BACKGROUND)) + start = length + } else if (atVarnameReference(lineText, start)) { + // variable + // references + + val buf = StringBuilder() + var i = start + syntaxData!!.varnameReference!!.length + // Call any consecutive letters a word + while (i < length && Character.isLetter(lineText[i])) { + buf.append(lineText[i]) + i++ + } + + // See if the word is a variable + if (isDefinedVariable(buf.toString())) { + // It's a keyword; create the StyleRange + styles.add(StyleRange(lineOffset + start, i - start, REFERENCED_VAR_COLOR, null, SWT.BOLD)) + } + // Move the marker to the last char (the one that wasn't a + // letter) + // so it can be retested in the next iteration through the loop + start = i + } else if (syntaxData!!.isPunctuation(lineText[start])) { + // Add range for punctuation + styles.add(StyleRange(lineOffset + start, 1, PUNCTUATION_COLOR, null)) + ++start + } else if (Character.isLetter(lineText[start])) { + + val kwEnd = getKeywordEnd(lineText, start) + // is a keyword + + if (kwEnd > start) { + styles.add(StyleRange(lineOffset + start, kwEnd - start, KEYWORD_COLOR, null)) + } + + // Move the marker to the last char (the one that wasn't a + // letter) + // so it can be retested in the next iteration through the loop + start = Math.abs(kwEnd) + } else { + ++start // It's nothing we're interested in; advance the marker + }// Check for punctuation + } + + return styles + } + + companion object { + + private val COMMENT_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN) + private val REFERENCED_VAR_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN) + private val UNREFERENCED_VAR_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW) + private val COMMENT_BACKGROUND = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE) + private val PUNCTUATION_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_BLACK) + private val KEYWORD_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_MAGENTA) + private val STRING_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE) + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxData.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxData.kt new file mode 100644 index 0000000..88e0f9d --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxData.kt @@ -0,0 +1,37 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +/** + * This class contains information for syntax coloring and styling for an + * extension + */ +class SyntaxData(val extension: String) { + + var varnameReference: String? = null + var stringStart: String? = null + var stringEnd: String? = null + private var keywords: Collection? = null + private var punctuation: String? = null + var comment: String? = null + var multiLineCommentStart: String? = null + var multiLineCommentEnd: String? = null + + fun matches(otherExtension: String): Boolean { + return extension == otherExtension + } + + fun isKeyword(word: String): Boolean { + return keywords != null && keywords!!.contains(word) + } + + fun isPunctuation(ch: Char): Boolean { + return punctuation != null && punctuation!!.indexOf(ch) >= 0 + } + + fun setKeywords(keywords: Collection) { + this.keywords = keywords + } + + fun setPunctuation(thePunctuationChars: String) { + punctuation = thePunctuationChars + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxManager.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxManager.kt new file mode 100644 index 0000000..570e4bc --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxManager.kt @@ -0,0 +1,94 @@ +package com.alibaba.smartfox.eclipse.ui.pmd + +import org.eclipse.swt.custom.StyledText +import org.eclipse.swt.events.ModifyListener +import java.util.HashSet +import java.util.Hashtable +import java.util.MissingResourceException +import java.util.ResourceBundle +import java.util.StringTokenizer + +/** + * This class manages the syntax coloring and styling data + */ +object SyntaxManager { + + private val syntaxByExtension = Hashtable() + + fun adapt(codeField: StyledText, languageCode: String, oldListener: ModifyListener?): ModifyListener? { + + if (oldListener != null) { + codeField.removeModifyListener(oldListener) + } + + val sd = SyntaxManager.getSyntaxData(languageCode) ?: return null + + val blsl = BasicLineStyleListener(sd) + codeField.addLineStyleListener(blsl) + + val ml = ModifyListener { + blsl.refreshMultilineComments(codeField.text) + codeField.redraw() + } + codeField.addModifyListener(ml) + + return ml + } + + /** + * Gets the syntax data for an extension + */ + @Synchronized fun getSyntaxData(extension: String): SyntaxData? { + // Check in cache + var sd: SyntaxData? = syntaxByExtension[extension] + if (sd == null) { + // Not in cache; load it and put in cache + sd = loadSyntaxData(extension) + if (sd != null) { + syntaxByExtension.put(sd.extension, sd) + } + } + return sd + } + + /** + * Loads the syntax data for an extension + * @return SyntaxData + */ + private fun loadSyntaxData(filename: String): SyntaxData? { + var sd: SyntaxData? = null + try { + val rb = ResourceBundle.getBundle("/syntax/$filename") + sd = SyntaxData(filename) + + sd.stringStart = rb.getString("stringstart") + sd.stringEnd = rb.getString("stringend") + sd.multiLineCommentStart = rb.getString("multilinecommentstart") + sd.multiLineCommentEnd = rb.getString("multilinecommentend") + + // Load the keywords + val keywords = HashSet() + val st = StringTokenizer(rb.getString("keywords"), " ") + while (st.hasMoreTokens()) { + keywords.add(st.nextToken()) + } + sd.setKeywords(keywords) + + // Load the punctuation + sd.setPunctuation(rb.getString("punctuation")) + + if (rb.containsKey("comment")) { + sd.comment = rb.getString("comment") + } + + if (rb.containsKey("varnamedelimiter")) { + sd.varnameReference = rb.getString("varnamedelimiter") + } + + } catch (e: MissingResourceException) { + // Ignore + } + + return sd + } +} diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/CleanUps.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/CleanUps.kt new file mode 100644 index 0000000..a5eba1d --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/CleanUps.kt @@ -0,0 +1,361 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2016, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== +package com.alibaba.smartfox.eclipse.util + +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import org.eclipse.core.filebuffers.FileBuffers +import org.eclipse.core.filebuffers.ITextFileBuffer +import org.eclipse.core.filebuffers.LocationKind +import org.eclipse.core.resources.IFile +import org.eclipse.core.runtime.Assert +import org.eclipse.core.runtime.CoreException +import org.eclipse.core.runtime.IProgressMonitor +import org.eclipse.core.runtime.IStatus +import org.eclipse.core.runtime.NullProgressMonitor +import org.eclipse.core.runtime.Status +import org.eclipse.jdt.core.ICompilationUnit +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.internal.corext.fix.CleanUpConstants +import org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring +import org.eclipse.jdt.internal.corext.fix.FixMessages +import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser +import org.eclipse.jdt.internal.ui.JavaPlugin +import org.eclipse.jdt.internal.ui.actions.ActionUtil +import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions +import org.eclipse.jdt.ui.SharedASTProvider +import org.eclipse.jdt.ui.cleanup.CleanUpContext +import org.eclipse.jdt.ui.cleanup.CleanUpOptions +import org.eclipse.jdt.ui.cleanup.ICleanUp +import org.eclipse.jface.text.BadLocationException +import org.eclipse.jface.text.IDocument +import org.eclipse.jface.text.IDocumentExtension4 +import org.eclipse.jface.window.Window +import org.eclipse.ltk.core.refactoring.Change +import org.eclipse.ltk.core.refactoring.CompositeChange +import org.eclipse.ltk.core.refactoring.IRefactoringCoreStatusCodes +import org.eclipse.ltk.core.refactoring.NullChange +import org.eclipse.ltk.core.refactoring.PerformChangeOperation +import org.eclipse.ltk.core.refactoring.RefactoringCore +import org.eclipse.ltk.core.refactoring.RefactoringStatus +import org.eclipse.ltk.core.refactoring.TextFileChange +import org.eclipse.ltk.ui.refactoring.RefactoringUI +import org.eclipse.text.edits.MalformedTreeException +import org.eclipse.text.edits.TextEdit +import org.eclipse.text.edits.UndoEdit +import org.eclipse.ui.PlatformUI +import java.util.ArrayList +import java.util.HashMap +import java.util.LinkedList + +/** + * + * + * @author caikang + * @date 2017/06/15 + */ +object CleanUps { + + private val WARNING_VALUE = "warning" + private val ERROR_VALUE = "error" + + val cleanUpSettings = mapOf(CleanUpConstants.ADD_MISSING_ANNOTATIONS to CleanUpOptions.TRUE, + CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_ALWAYS to CleanUpOptions.TRUE, + CleanUpConstants.ADD_MISSING_ANNOTATIONS_OVERRIDE to CleanUpOptions.TRUE, + CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS to CleanUpOptions.TRUE, + CleanUpConstants.ADD_MISSING_ANNOTATIONS_OVERRIDE_FOR_INTERFACE_METHOD_IMPLEMENTATION to + CleanUpOptions.TRUE) + + fun fix(file: IFile, monitor: IProgressMonitor) { + val compilationUnit = JavaCore.createCompilationUnitFrom(file) ?: return + doCleanUp(compilationUnit, monitor) + } + + @Throws(CoreException::class) fun doCleanUp(unit: ICompilationUnit, monitor: IProgressMonitor) { + + monitor.beginTask("Fix", IProgressMonitor.UNKNOWN) + + if (!ActionUtil.isOnBuildPath(unit)) return + val result = CompositeChange(FixMessages.CleanUpPostSaveListener_SaveAction_ChangeName) + val undoEdits = LinkedList() + val oldFileValue = unit.resource.modificationStamp + val oldDocValue = getDocumentStamp(unit.resource as IFile, monitor) + val manager = RefactoringCore.getUndoManager() + var success = false + + try { + manager.aboutToPerformChange(result) + success = doCleanUp(unit, monitor, result, undoEdits) + } finally { + manager.changePerformed(result, success) + } + + if (undoEdits.size > 0) { + val undoEditArray = undoEdits.toTypedArray() + val undo = CleanUpSaveUndo(result.name, unit.resource as IFile, undoEditArray, oldDocValue, oldFileValue) + undo.initializeValidationData(NullProgressMonitor()) + manager.addUndo(result.name, undo) + } + } + + @Throws(CoreException::class) private fun getDocumentStamp(file: IFile, monitor: IProgressMonitor): Long { + val manager = FileBuffers.getTextFileBufferManager() + val path = file.fullPath + + monitor.beginTask("", 2) + + var buffer: ITextFileBuffer? = null + try { + manager.connect(path, LocationKind.IFILE, monitor) + buffer = manager.getTextFileBuffer(path, LocationKind.IFILE) + val document = buffer!!.document + + if (document is IDocumentExtension4) { + return document.modificationStamp + } else { + return file.modificationStamp + } + } finally { + if (buffer != null) manager.disconnect(path, LocationKind.IFILE, monitor) + monitor.done() + } + } + + private fun doCleanUp(unit: ICompilationUnit, monitor: IProgressMonitor, result: CompositeChange, + undoEdits: LinkedList): Boolean { + val cleanUps = JavaPlugin.getDefault().cleanUpRegistry.createCleanUps( + setOf("org.eclipse.jdt.ui.cleanup.java50", "org.eclipse.jdt.ui.cleanup.control_statements")) + val preCondition = RefactoringStatus() + val postCondition = RefactoringStatus() + cleanUps.forEach { cleanUp -> + cleanUp.setOptions(MapCleanUpOptions(cleanUpSettings)) + + preCondition.merge(cleanUp.checkPreConditions(unit.javaProject, arrayOf(unit), monitor)) + + val options = HashMap(cleanUp.requirements.compilerOptions ?: emptyMap()) + + var ast = CleanUps.createAst(unit, options, monitor) + if (cleanUp.requirements.requiresAST()) { + ast = createAst(unit, options, monitor) + } + + val context = CleanUpContext(unit, ast) + + val undoneCleanUps = ArrayList() + val change = CleanUpRefactoring.calculateChange(context, arrayOf(cleanUp), undoneCleanUps, null) + + postCondition.merge(cleanUp.checkPostConditions(monitor)) + if (showStatus(postCondition) != Window.OK) { + return@doCleanUp false + } + + if (change == null) { + return@forEach + } + result.add(change) + + change.initializeValidationData(NullProgressMonitor()) + + val performChangeOperation = PerformChangeOperation(change) + performChangeOperation.setSchedulingRule(unit.schedulingRule) + performChangeOperation.run(monitor) + performChangeOperation.undoChange + undoEdits.addFirst(change.undoEdit) + } + + return true + } + + private fun showStatus(status: RefactoringStatus): Int { + if (!status.hasError()) return Window.OK + + val shell = PlatformUI.getWorkbench().activeWorkbenchWindow.shell + + val dialog = RefactoringUI.createRefactoringStatusDialog(status, shell, "", false) + return dialog.open() + } + + private fun createAst(unit: ICompilationUnit, cleanUpOptions: Map, + monitor: IProgressMonitor): CompilationUnit { + val project = unit.javaProject + if (compatibleOptions(project, cleanUpOptions)) { + val ast = SharedASTProvider.getAST(unit, SharedASTProvider.WAIT_NO, monitor) + if (ast != null) return ast + } + + val parser = CleanUpRefactoring.createCleanUpASTParser() + parser.setSource(unit) + + val compilerOptions = RefactoringASTParser.getCompilerOptions(unit.javaProject) + compilerOptions.putAll(cleanUpOptions) + parser.setCompilerOptions(compilerOptions) + + return parser.createAST(monitor) as CompilationUnit + } + + private fun compatibleOptions(project: IJavaProject, cleanUpOptions: Map): Boolean { + if (cleanUpOptions.isEmpty()) { + return true + } + + val projectOptions = project.getOptions(true) + + return !cleanUpOptions.keys.any { + val projectOption = projectOptions[it]?.toString() + val cleanUpOption = cleanUpOptions[it]?.toString() + !strongerEquals(projectOption, cleanUpOption) + } + } + + private fun strongerEquals(projectOption: String?, cleanUpOption: String?): Boolean { + if (projectOption == null) return false + + if (ERROR_VALUE == cleanUpOption) { + return ERROR_VALUE == projectOption + } else if (WARNING_VALUE == cleanUpOption) { + return ERROR_VALUE == projectOption || WARNING_VALUE == projectOption + } + + return false + } + + private class CleanUpSaveUndo(name: String, private val fFile: IFile, private val fUndos: Array, + private val fDocumentStamp: Long, private val fFileStamp: Long) : TextFileChange(name, + fFile) { + + init { + Assert.isNotNull(fUndos) + } + + public override fun needsSaving(): Boolean { + return true + } + + @Throws(CoreException::class) override fun perform(monitor: IProgressMonitor?): Change { + val pm = monitor ?: NullProgressMonitor() + if (isValid(pm).hasFatalError()) return NullChange() + + val manager = FileBuffers.getTextFileBufferManager() + pm.beginTask("", 2) + var buffer: ITextFileBuffer? = null + + try { + manager.connect(fFile.fullPath, LocationKind.IFILE, pm) + buffer = manager.getTextFileBuffer(fFile.fullPath, LocationKind.IFILE) + + val document = buffer!!.document + val oldFileValue = fFile.modificationStamp + val undoEditCollector = LinkedList() + val oldDocValue = LongArray(1) + val setContentStampSuccess = booleanArrayOf(false) + + if (!buffer.isSynchronizationContextRequested) { + performEdit(document, oldFileValue, undoEditCollector, oldDocValue, setContentStampSuccess) + + } else { + val fileBufferManager = FileBuffers.getTextFileBufferManager() + + class UIRunnable : Runnable { + var fDone: Boolean = false + var fException: Exception? = null + + override fun run() { + synchronized(this) { + try { + performEdit(document, oldFileValue, undoEditCollector, oldDocValue, + setContentStampSuccess) + } catch (e: BadLocationException) { + fException = e + } catch (e: MalformedTreeException) { + fException = e + } catch (e: CoreException) { + fException = e + } finally { + fDone = true + (this as Object).notifyAll() + } + } + } + } + + val runnable = UIRunnable() + + synchronized(runnable) { + fileBufferManager.execute(runnable) + while (!runnable.fDone) { + try { + (runnable as Object).wait(500) + } catch (x: InterruptedException) { + } + + } + } + + if (runnable.fException != null) { + if (runnable.fException is BadLocationException) { + throw runnable.fException as BadLocationException + } else if (runnable.fException is MalformedTreeException) { + throw runnable.fException as MalformedTreeException + } else if (runnable.fException is CoreException) { + throw runnable.fException as CoreException + } + } + } + + buffer.commit(pm, false) + if (!setContentStampSuccess[0]) { + fFile.revertModificationStamp(fFileStamp) + } + + return CleanUpSaveUndo(name, fFile, undoEditCollector.toTypedArray(), oldDocValue[0], oldFileValue) + } catch (e: BadLocationException) { + throw wrapBadLocationException(e) + } finally { + if (buffer != null) manager.disconnect(fFile.fullPath, LocationKind.IFILE, pm) + } + } + + @Throws(MalformedTreeException::class, BadLocationException::class, + CoreException::class) private fun performEdit(document: IDocument, oldFileValue: Long, + editCollector: LinkedList, + oldDocValue: LongArray, + setContentStampSuccess: BooleanArray) { + if (document is IDocumentExtension4) { + oldDocValue[0] = document.modificationStamp + } else { + oldDocValue[0] = oldFileValue + } + + // perform the changes + fUndos.map { it.apply(document, TextEdit.CREATE_UNDO) }.forEach { editCollector.addFirst(it) } + + if (document is IDocumentExtension4 && fDocumentStamp != IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { + try { + document.replace(0, 0, "", fDocumentStamp) + setContentStampSuccess[0] = true + } catch (e: BadLocationException) { + throw wrapBadLocationException(e) + } + + } + } + } + + private fun wrapBadLocationException(e: BadLocationException): CoreException { + var message: String? = e.message + if (message == null) message = "BadLocationException" + return CoreException( + Status(IStatus.ERROR, SmartfoxActivator.PLUGIN_ID, IRefactoringCoreStatusCodes.BAD_LOCATION, message, + e)) + } + +} \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/MarkerUtil.kt b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/MarkerUtil.kt new file mode 100644 index 0000000..923f511 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/MarkerUtil.kt @@ -0,0 +1,136 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2016, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== +package com.alibaba.smartfox.eclipse.util + +import com.alibaba.smartfox.eclipse.QuickFixGenerator +import com.alibaba.smartfox.eclipse.SmartfoxActivator +import com.google.common.io.Files +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RulePriority +import net.sourceforge.pmd.RuleViolation +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IMarker +import org.eclipse.core.resources.IProject +import org.eclipse.core.resources.IResource +import org.eclipse.core.runtime.CoreException +import org.eclipse.jface.text.BadLocationException +import org.eclipse.jface.text.Document +import org.eclipse.ui.IMarkerResolution +import java.nio.charset.Charset + +/** + * @author caikang + * @date 2017/06/08 + */ +object MarkerUtil { + + private val PMD_TAB_SIZE = 8 + + private val MARKER_TYPE = "${SmartfoxActivator.PLUGIN_ID}.p3cMarker" + + @Throws(CoreException::class) + fun removeAllMarkers(file: IFile) { + try { + if (file.exists()) { + file.deleteMarkers(MARKER_TYPE, true, IResource.DEPTH_ZERO) + } + } catch (e: Exception) { + SmartfoxActivator.instance.logError(e.message ?: "", e) + } + } + + @Throws(CoreException::class) + fun removeAllMarkers(project: IProject) { + project.deleteMarkers(MARKER_TYPE, true, IResource.DEPTH_INFINITE) + } + + @Throws(CoreException::class) + fun addMarker(file: IFile, violation: RuleViolation): IMarker { + + val marker = file.createMarker(MARKER_TYPE) + marker.setAttribute(IMarker.MESSAGE, violation.description) + val severity = when (violation.rule.priority) { + RulePriority.HIGH -> IMarker.SEVERITY_ERROR + RulePriority.MEDIUM_HIGH -> IMarker.SEVERITY_WARNING + else -> IMarker.SEVERITY_INFO + } + marker.setRule(violation.rule.name) + marker.setAttribute(IMarker.SEVERITY, severity) + marker.setAttribute(IMarker.LINE_NUMBER, Math.max(violation.beginLine, 0)) + val range = getAbsoluteRange(file, violation) + val start = Math.max(range.start, 0) + marker.setAttribute(IMarker.CHAR_START, start) + val end = Math.max(range.end, 0) + marker.setAttribute(IMarker.CHAR_END, end) + return marker + } + + + fun getAbsoluteRange(file: IFile, violation: RuleViolation): Range { + val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) + try { + return calculateAbsoluteRange(content, violation) + } catch (e: BadLocationException) { + return Range(0, 0) + } + } + + @Throws(BadLocationException::class) private fun calculateAbsoluteRange(content: String, + violation: RuleViolation): Range { + val document = Document(content) + + // violation line and column start at one, the marker's start and end positions at zero + val start = getAbsolutePosition(content, document.getLineOffset(violation.beginLine - 1), violation.beginColumn) + val end = getAbsolutePosition(content, document.getLineOffset(violation.endLine - 1), violation.endColumn) + + // for some rules PMD creates violations with the end position before the start position + val range = if (start <= end) { + Range(start - 1, end) + } else { + Range(end - 1, start) + } + + return range + } + + private fun getAbsolutePosition(content: String, lineOffset: Int, pmdCharOffset: Int): Int { + var pmdCharCounter = 0 + var absoluteOffset = lineOffset + while (pmdCharCounter < pmdCharOffset) { + if (absoluteOffset < content.length) { + val c = content[absoluteOffset] + if (c == '\t') { + pmdCharCounter = (pmdCharCounter / PMD_TAB_SIZE + 1) * PMD_TAB_SIZE + } else { + pmdCharCounter++ + } + } else { + break + } + absoluteOffset++ + } + return absoluteOffset + } +} + +fun IMarker.setRule(rule: String) { + this.setAttribute("rule", rule) +} + +fun IMarker.getRule(): Rule { + return SmartfoxActivator.instance.getRule(this.getAttribute("rule") as String) +} + +fun IMarker.getResolution(): IMarkerResolution? { + return QuickFixGenerator.quickFixes[getRule().name] +} + +data class Range(val start: Int, val end: Int) diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle.xml new file mode 100644 index 0000000..755afb5 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle.xml @@ -0,0 +1,23 @@ + + + + 阿里编码规约扫描 + + 切换语言至英文(English) + 切换语言至中文 + 切换到English成功,是否重启 + 切换到中文成功,是否重启 + + + + + + + + + + + \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle_en.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle_en.xml new file mode 100644 index 0000000..091d2a7 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle_en.xml @@ -0,0 +1,25 @@ + + + + Alibaba Coding Guidelines Analyze + Switch language to English + Switch language to Chinese + + Switch language to English success,restart? + Switch language to Chinese success,restart? + + + + + + + + + + + \ No newline at end of file diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-pmd.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-pmd.xml new file mode 100644 index 0000000..3ae5d73 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-pmd.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-ruleOnEclipse.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-ruleOnEclipse.xml new file mode 100644 index 0000000..edbc1e5 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-ruleOnEclipse.xml @@ -0,0 +1,42 @@ + + + + + + rule.standalone.MissingOverrideAnnotationRule.desc + 1 + + + + + + + 1 + + + rule.standalone.AvoidUseDeprecationRule.desc + 2 + + + + rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.desc + 1 + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/syntax/java.properties b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/syntax/java.properties new file mode 100644 index 0000000..f1c6ed4 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/syntax/java.properties @@ -0,0 +1,8 @@ +# This file contains the syntax data for .java files +comment=// +stringstart=" +stringend=" +multilinecommentstart=/* +multilinecommentend=*/ +punctuation=(){};:?<>=+-*/&|~!%.[] +keywords=abstract assert boolean break byte case catch char class const continue do double else enum extends false final finally for if implements import int interface native new null package protected public private return static strictfp super switch synchronized this throws true try void volatile while diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/category.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/category.xml new file mode 100644 index 0000000..15c89a2 --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/category.xml @@ -0,0 +1,10 @@ + + + + Alibaba Java Coding Guidelines + + + + + + diff --git a/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/pom.xml b/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/pom.xml new file mode 100644 index 0000000..ca7efcb --- /dev/null +++ b/eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + com.alibaba.smartfox.eclipse + smartfox-eclipse + 1.0.0-SNAPSHOT + + com.alibaba.smartfox.eclipse.updatesite + eclipse-repository + 2017 + + + publish-to-update-site + + + + org.eclipse.tycho.extras + tycho-p2-extras-plugin + + + publish + install + + mirror + + + + + ${project.build.directory}/repository + + + true + ${eclipse.updatesite.path} + + + + + + + + + diff --git a/eclipse-plugin/doc/images/analyze_result.png b/eclipse-plugin/doc/images/analyze_result.png new file mode 100644 index 0000000..aad166b Binary files /dev/null and b/eclipse-plugin/doc/images/analyze_result.png differ diff --git a/eclipse-plugin/doc/images/eclipse_analyze.png b/eclipse-plugin/doc/images/eclipse_analyze.png new file mode 100644 index 0000000..92e933c Binary files /dev/null and b/eclipse-plugin/doc/images/eclipse_analyze.png differ diff --git a/eclipse-plugin/doc/images/eclipse_switch_language.png b/eclipse-plugin/doc/images/eclipse_switch_language.png new file mode 100644 index 0000000..e8f2184 Binary files /dev/null and b/eclipse-plugin/doc/images/eclipse_switch_language.png differ diff --git a/eclipse-plugin/doc/images/install.png b/eclipse-plugin/doc/images/install.png new file mode 100644 index 0000000..5a60033 Binary files /dev/null and b/eclipse-plugin/doc/images/install.png differ diff --git a/eclipse-plugin/pom.xml b/eclipse-plugin/pom.xml new file mode 100644 index 0000000..99f1a90 --- /dev/null +++ b/eclipse-plugin/pom.xml @@ -0,0 +1,338 @@ + + + 4.0.0 + com.alibaba.smartfox.eclipse + smartfox-eclipse + 1.0.0-SNAPSHOT + pom + 2017 + + 1.0.0 + ${tycho.version} + http://download.eclipse.org/releases/neon + UTF-8 + http://download.eclipse.org/tools/ajdt/46/dev/update + 1.1.51 + juno + + + com.alibaba.smartfox.eclipse.plugin + com.alibaba.smartfox.eclipse.feature + com.alibaba.smartfox.eclipse.updatesite + + + + junit + junit + 4.11 + test + + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + compile + + + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho.version} + true + + + org.eclipse.tycho + target-platform-configuration + + + + linux + gtk + x86 + + + linux + gtk + x86_64 + + + win32 + win32 + x86 + + + win32 + win32 + x86_64 + + + macosx + cocoa + x86_64 + + + + + + + + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho.version} + + 1.7 + + + + UTF-8 + + -err:-forbidden + + + + org.codehaus.mojo + aspectj-maven-plugin + ${aspectj.plugin.version} + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + org.eclipse.tycho + tycho-packaging-plugin + ${tycho.version} + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + true + + + + org.eclipse.tycho + tycho-source-plugin + ${tycho.version} + + false + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho.version} + + + org.eclipse.tycho + tycho-p2-repository-plugin + ${tycho.version} + + smartfox-eclipse-plugin + + + + org.eclipse.tycho.extras + tycho-source-feature-plugin + ${tycho-extras.version} + + + org.eclipse.tycho.extras + tycho-custom-bundle-plugin + ${tycho-extras.version} + + + org.eclipse.tycho + tycho-p2-plugin + ${tycho.version} + + warn + none + + + http://download.eclipse.org/eclipse/updates/4.4 + + + + + + + + + + + macosx-jvm-flags + + + mac + + + + + -XstartOnFirstThread + + + + + + eclipse-juno + + + !eclipse-release + + + + + juno + p2 + http://mirrors.ustc.edu.cn/eclipse/releases/juno/ + + + swtbot + p2 + http://mirrors.ustc.edu.cn/eclipse/technology/swtbot/releases/2.5.0/ + + + + + + eclipse-kepler + + + eclipse-release + kepler + + + + + kepler + p2 + http://download.eclipse.org/releases/kepler/ + + + swtbot + p2 + http://download.eclipse.org/technology/swtbot/releases/2.3.0/ + + + + + + eclipse-luna + + + eclipse-release + luna + + + + + luna + p2 + http://download.eclipse.org/releases/luna/ + + + swtbot + p2 + http://download.eclipse.org/technology/swtbot/releases/2.3.0/ + + + + + + eclipse-mars + + + eclipse-release + mars + + + + + mars + p2 + http://download.eclipse.org/releases/mars/ + + + swtbot + p2 + http://download.eclipse.org/technology/swtbot/releases/2.3.0/ + + + + + + eclipse-neon + + + eclipse-release + neon + + + + + neon + p2 + http://download.eclipse.org/releases/neon/ + + + swtbot + p2 + http://download.eclipse.org/technology/swtbot/releases/2.3.0/ + + + + + + diff --git a/idea-plugin/.gitignore b/idea-plugin/.gitignore new file mode 100644 index 0000000..b0bfb71 --- /dev/null +++ b/idea-plugin/.gitignore @@ -0,0 +1,94 @@ +# Gradle +build +.gradle + +testdata/ +# Java gitignore # +.class +.log + +# Package Files # + +*.war +*.ear + +#hsf files +configuration + +# maven gitignore# +target/** + +.svn/ + +# intelliJ.gitignore # +.idea +*.iml +*.ipr +*.iws + +# Eclipse git ignore# +*.pydevproject +.project +.metadata +bin/** +*/bin/** +tmp/** +tmp/**/* +configuration/** +*.tmp +*.bak +*.orig +*.swp +*~.nib +.classpath +.settings/ +.loadpath +.fileTable* +.cache + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +#log +*.log +*.log.* + +# Windows Thumbs.db +*.db + +# OSX +.DS_Store + +# sass gitignore# +.sass-cache +.idea + +# tcc_coverage +coverage.ec + + + +config.client.* + +temp/ +*.pid +*.orig + +hsf.configuration/ + +# code coverage report +*.ec + +#hsf test +*.instance +out +**/idea-sandbox \ No newline at end of file diff --git a/idea-plugin/README.md b/idea-plugin/README.md new file mode 100644 index 0000000..09d8e85 --- /dev/null +++ b/idea-plugin/README.md @@ -0,0 +1,62 @@ +# Idea Plugin +--- +## Prepare +- Project JDK: 1.7+ +- Gradle: 3.0+(Require JDK1.8+ for gradle) + +## Build +``` +cd p3c-idea +gradle clean buildPlugin +``` + +## Run plugin + +``` +cd p3c-idea +gradle runIde +# run specific IDEA +gradle runIde -Pidea_version=14.1.7 +``` + +## Use p3c-common as your plugin dependency +``` groovy +compile 'com.alibaba.p3c.idea:p3c-common:1.0.0' +``` + +## Install + +1. Settings >> Plugins >> Browse repositories... + + ![Switch language](doc/images/install_1.png) + +2. Search plugin by keyword 'alibaba' then install 'Alibaba Java Coding Guidelines' plugin + + ![Switch language](doc/images/install_2.png) + +3. Restart to take effect. + +## Use + +1. Switch language + + ![Switch language](doc/images/switch_language.png) + +2. Inspections + + ![Real time](doc/images/inspection.png) + + ![Settings](doc/images/inspection_setting.png) + +3. Code Analyze + + ![Settings](doc/images/analyze.png) + + We use the idea standard Inspection Results to show our violations. + + ![Result](doc/images/inspection_result.png) + + We can also analyze file which is modified before vsc checkin. + + ![Before Checkin](doc/images/analyze_before_checkin.png) + \ No newline at end of file diff --git a/idea-plugin/build.gradle b/idea-plugin/build.gradle new file mode 100644 index 0000000..abe8684 --- /dev/null +++ b/idea-plugin/build.gradle @@ -0,0 +1,38 @@ +buildscript { + repositories { + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } + maven { + url 'http://dl.bintray.com/jetbrains/intellij-plugin-service' + } + mavenCentral() + + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + group 'com.alibaba.p3c.idea' + apply plugin: 'java' + apply plugin: 'kotlin' + apply plugin: 'maven-publish' + + sourceCompatibility = 1.7 + compileJava.options.encoding = 'UTF-8' + configurations.all { + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + } + repositories { + jcenter() + mavenCentral() + } + + dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + testCompile group: 'junit', name: 'junit', version: '4.11' + } +} + diff --git a/idea-plugin/doc/images/analyze.png b/idea-plugin/doc/images/analyze.png new file mode 100644 index 0000000..eaf0a65 Binary files /dev/null and b/idea-plugin/doc/images/analyze.png differ diff --git a/idea-plugin/doc/images/analyze_before_checkin.png b/idea-plugin/doc/images/analyze_before_checkin.png new file mode 100644 index 0000000..7871806 Binary files /dev/null and b/idea-plugin/doc/images/analyze_before_checkin.png differ diff --git a/idea-plugin/doc/images/inspection.png b/idea-plugin/doc/images/inspection.png new file mode 100644 index 0000000..1a676dd Binary files /dev/null and b/idea-plugin/doc/images/inspection.png differ diff --git a/idea-plugin/doc/images/inspection_result.png b/idea-plugin/doc/images/inspection_result.png new file mode 100644 index 0000000..856f9ab Binary files /dev/null and b/idea-plugin/doc/images/inspection_result.png differ diff --git a/idea-plugin/doc/images/inspection_setting.png b/idea-plugin/doc/images/inspection_setting.png new file mode 100644 index 0000000..e86f12c Binary files /dev/null and b/idea-plugin/doc/images/inspection_setting.png differ diff --git a/idea-plugin/doc/images/install_1.png b/idea-plugin/doc/images/install_1.png new file mode 100644 index 0000000..f74b736 Binary files /dev/null and b/idea-plugin/doc/images/install_1.png differ diff --git a/idea-plugin/doc/images/install_2.png b/idea-plugin/doc/images/install_2.png new file mode 100644 index 0000000..0320a4e Binary files /dev/null and b/idea-plugin/doc/images/install_2.png differ diff --git a/idea-plugin/doc/images/switch_language.png b/idea-plugin/doc/images/switch_language.png new file mode 100644 index 0000000..385cb12 Binary files /dev/null and b/idea-plugin/doc/images/switch_language.png differ diff --git a/idea-plugin/gradle.properties b/idea-plugin/gradle.properties new file mode 100644 index 0000000..9bf13d1 --- /dev/null +++ b/idea-plugin/gradle.properties @@ -0,0 +1,9 @@ +kotlin_version=1.1.51 +#idea_version=171.3780.15 +idea_version=14.1.7 +plugin_name=Alibaba Java Coding Guidelines +pmd_version=5.5.2 +gradle_jetbrains_version=0.2.13 +systemProp.file.encoding=UTF-8 + +plugin_version=1.0.0 diff --git a/idea-plugin/gradle/wrapper/gradle-wrapper.jar b/idea-plugin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..51288f9 Binary files /dev/null and b/idea-plugin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/idea-plugin/gradle/wrapper/gradle-wrapper.properties b/idea-plugin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..143e48e --- /dev/null +++ b/idea-plugin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Nov 30 15:31:46 CST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip diff --git a/idea-plugin/gradlew b/idea-plugin/gradlew new file mode 100755 index 0000000..4453cce --- /dev/null +++ b/idea-plugin/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/idea-plugin/gradlew.bat b/idea-plugin/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/idea-plugin/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/idea-plugin/p3c-common/build.gradle b/idea-plugin/p3c-common/build.gradle new file mode 100644 index 0000000..f90d52e --- /dev/null +++ b/idea-plugin/p3c-common/build.gradle @@ -0,0 +1,94 @@ +plugins { + id "org.jetbrains.intellij" version '0.2.17' +} +apply plugin: 'kotlin' +apply plugin: 'idea' +apply plugin: 'maven' +apply plugin: 'signing' + +javadoc { + options.tags = [ "date" ] +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from 'build/docs/javadoc' +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} + +artifacts { + archives jar + archives javadocJar + archives sourcesJar +} + +intellij { + version idea_version + pluginName plugin_name + updateSinceUntilBuild false + sandboxDirectory "$project.buildDir/idea-sandbox/$idea_version" +} +version '1.0.0' + +dependencies { + compile group: 'org.freemarker', name: 'freemarker', version: '2.3.25-incubating' + compile 'com.alibaba.p3c:p3c-pmd:1.3.0' + compile group: 'org.javassist', name: 'javassist', version: '3.21.0-GA' +} + +uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'p3c-common' + packaging 'jar' + description 'P3c Idea Plugin Common.' + url 'https://github.com/alibaba/p3c' + + scm { + url 'https://github.com/alibaba/p3c' + connection 'scm:git:https://git@github.com/alibaba/p3c.git' + } + + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + + developers { + developer { + id 'junlie' + name 'Junlie' + email 'sean.caikang@gmail.com' + } + developer { + id 'ZengHou' + name 'ZengHou' + email 'fengwei1983@gmail.com' + } + } + } + } + } +} + +signing { + sign configurations.archives +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/NumberConstants.java b/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/NumberConstants.java new file mode 100644 index 0000000..7a3610e --- /dev/null +++ b/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/NumberConstants.java @@ -0,0 +1,29 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea; + +/** + * @author caikang + * @date 2016/12/28 + */ +public interface NumberConstants { + int INTEGER_SIZE_OR_LENGTH_0 = 0; + int INTEGER_SIZE_OR_LENGTH_1 = 1; + int INTEGER_SIZE_OR_LENGTH_2 = 2; + + int INDEX_0 = 0; + int INDEX_1 = 1; +} diff --git a/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/ObjectConstants.java b/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/ObjectConstants.java new file mode 100644 index 0000000..2e09e38 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/java/com/alibaba/p3c/idea/ObjectConstants.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea; + +/** + * @author caikang + * @date 2016/12/28 + */ +public interface ObjectConstants { + String METHOD_NAME_EQUALS = "equals"; + String METHOD_NAME_HASHCODE = "hashCode"; + String METHOD_NAME_ADD = "add"; + String METHOD_NAME_PUT = "put"; + String CLASS_LITERAL = "class"; + String INTERFACE_LITERAL = "interface"; + String ENUM_LITERAL = "enum"; +} diff --git a/idea-plugin/p3c-common/src/main/java/icons/P3cIcons.java b/idea-plugin/p3c-common/src/main/java/icons/P3cIcons.java new file mode 100644 index 0000000..71a3d6a --- /dev/null +++ b/idea-plugin/p3c-common/src/main/java/icons/P3cIcons.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package icons; + +import javax.swing.Icon; + +import com.intellij.openapi.util.IconLoader; + +/** + * @author caikang + * @date 2016/12/28 + */ +public interface P3cIcons { + Icon ANALYSIS_ACTION = IconLoader.getIcon("/icons/ali-ide-run.png"); + + Icon PROJECT_INSPECTION_ON = IconLoader.getIcon("/icons/qiyong.png"); + Icon PROJECT_INSPECTION_OFF = IconLoader.getIcon("/icons/tingyong.png"); + Icon LANGUAGE = IconLoader.getIcon("/icons/language.png"); + Icon ALIBABA = IconLoader.getIcon("/icons/alibaba.png"); +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt new file mode 100644 index 0000000..260a232 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt @@ -0,0 +1,193 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.action + +import com.alibaba.p3c.idea.compatible.inspection.InspectionProfileService +import com.alibaba.p3c.idea.compatible.inspection.Inspections +import com.alibaba.p3c.idea.ep.InspectionActionExtensionPoint +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.beust.jcommander.internal.Lists +import com.intellij.analysis.AnalysisScope +import com.intellij.analysis.AnalysisUIOptions +import com.intellij.analysis.BaseAnalysisActionDialog +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl +import com.intellij.codeInspection.ex.InspectionManagerEx +import com.intellij.codeInspection.ex.InspectionToolWrapper +import com.intellij.codeInspection.ui.InspectionResultsView +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.DataKeys +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import java.awt.event.KeyEvent + +/** + * @author caikang + * @date 2016/12/11 + */ +class AliInspectionAction : AnAction() { + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val analysisUIOptions = ServiceManager.getService(project, AnalysisUIOptions::class.java)!! + analysisUIOptions.GROUP_BY_SEVERITY = true + + val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx + val toolWrappers = Inspections.aliInspections(project) { + it.tool is AliBaseInspection + } + val psiElement = e.getData(DataKeys.PSI_ELEMENT) + val psiFile = e.getData(DataKeys.PSI_FILE) + val virtualFile = e.getData(DataKeys.VIRTUAL_FILE) + val virtualFiles = e.getData>(CommonDataKeys.VIRTUAL_FILE_ARRAY) + var analysisScope: AnalysisScope? = null + var projectDir = false + if (psiFile != null) { + analysisScope = AnalysisScope(psiFile) + projectDir = isBaseDir(psiFile.virtualFile, project) + } else if (virtualFiles != null && virtualFiles.size > com.alibaba.p3c.idea.NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { + analysisScope = AnalysisScope(project, Lists.newArrayList(*virtualFiles)) + projectDir = virtualFiles.any { + isBaseDir(it, project) + } + } else { + if (virtualFile != null && virtualFile.isDirectory) { + val psiDirectory = PsiManager.getInstance(project).findDirectory(virtualFile) + if (psiDirectory != null) { + analysisScope = AnalysisScope(psiDirectory) + projectDir = isBaseDir(virtualFile, project) + } + } + if (analysisScope == null && virtualFile != null) { + analysisScope = AnalysisScope(project, listOf(virtualFile)) + projectDir = isBaseDir(virtualFile, project) + } + if (analysisScope == null) { + projectDir = true + analysisScope = AnalysisScope(project) + } + } + if (e.inputEvent is KeyEvent) { + inspectForKeyEvent(project, managerEx, toolWrappers, psiElement, psiFile, virtualFile, analysisScope) + return + } + val element = psiFile ?: psiElement + analysisScope.isIncludeTestSource = false + analysisScope.setSearchInLibraries(true) + createContext(toolWrappers, managerEx, element, + projectDir).doInspections(analysisScope) + } + + private fun isBaseDir(file: VirtualFile, project: Project): Boolean { + if (file.canonicalPath == null || project.basePath == null) { + return false + } + return project.basePath == file.canonicalPath + } + + private fun inspectForKeyEvent(project: Project, managerEx: InspectionManagerEx, + toolWrappers: List>, psiElement: PsiElement?, psiFile: PsiFile?, + virtualFile: VirtualFile?, analysisScope: AnalysisScope) { + var module: Module? = null + if (virtualFile != null && project.baseDir != virtualFile) { + module = ModuleUtilCore.findModuleForFile(virtualFile, project) + } + + val uiOptions = AnalysisUIOptions.getInstance(project) + uiOptions.ANALYZE_TEST_SOURCES = false + val dialog = BaseAnalysisActionDialog("Select Analyze Scope", "Analyze Scope", project, analysisScope, + if (module != null) module.name else null, true, uiOptions, psiElement) + + if (!dialog.showAndGet()) { + return + } + val scope = dialog.getScope(uiOptions, analysisScope, project, module) + scope.setSearchInLibraries(true) + val element = psiFile ?: psiElement + createContext(toolWrappers, managerEx, element, + dialog.isProjectScopeSelected).doInspections(scope) + } + + override fun update(e: AnActionEvent) { + e.presentation.text = P3cBundle.getMessage("com.alibaba.p3c.idea.action.AliInspectionAction.text") + } + + companion object { + val logger = Logger.getInstance(AliInspectionAction::class.java) + + fun createContext(toolWrapperList: List>, + managerEx: InspectionManagerEx, psiElement: PsiElement?, projectScopeSelected: Boolean): + GlobalInspectionContextImpl { + val model = InspectionProfileService.createSimpleProfile(toolWrapperList, managerEx, psiElement) + val inspectionContext = createNewGlobalContext( + managerEx, projectScopeSelected) + InspectionProfileService.setExternalProfile(model, inspectionContext) + return inspectionContext + } + + private fun createNewGlobalContext(managerEx: InspectionManagerEx, + projectScopeSelected: Boolean): GlobalInspectionContextImpl { + return object : GlobalInspectionContextImpl(managerEx.project, managerEx.contentManager) { + override fun runTools(scope: AnalysisScope, runGlobalToolsOnly: Boolean, + isOfflineInspections: Boolean) { + super.runTools(scope, runGlobalToolsOnly, isOfflineInspections) + if (myProgressIndicator.isCanceled) { + return + } + InspectionActionExtensionPoint.extension.extensions.forEach { + try { + it.doOnInspectionFinished(this, projectScopeSelected) + } catch(e: Exception) { + logger.warn(e) + } + } + } + + override fun close(noSuspiciousCodeFound: Boolean) { + super.close(noSuspiciousCodeFound) + InspectionActionExtensionPoint.extension.extensions.forEach { + try { + it.doOnClose(noSuspiciousCodeFound, project) + } catch(e: Exception) { + logger.warn(e) + } + } + } + + override fun addView(view: InspectionResultsView) { + super.addView(view) + InspectionActionExtensionPoint.extension.extensions.forEach { + try { + it.doOnView(view) + } catch(e: Exception) { + logger.warn(e) + } + } + } + } + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/SwitchLanguageAction.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/SwitchLanguageAction.kt new file mode 100644 index 0000000..2fded43 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/SwitchLanguageAction.kt @@ -0,0 +1,54 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.action + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.smartfox.idea.common.util.BalloonNotifications +import com.alibaba.smartfox.idea.common.util.getService +import com.intellij.notification.NotificationListener +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.ex.ApplicationManagerEx +import com.intellij.openapi.project.DumbAware + +/** + * + * + * @author caikang + * @date 2017/06/20 + */ +class SwitchLanguageAction : AnAction(), DumbAware { + val p3cConfig = P3cConfig::class.java.getService() + + val textKey = "com.alibaba.p3c.action.switch_language.text" + + override fun actionPerformed(e: AnActionEvent) { + p3cConfig.locale = when (p3cConfig.locale) { + P3cConfig.localeZh -> P3cConfig.localeEn + else -> P3cConfig.localeZh + } + BalloonNotifications.showSuccessNotification(P3cBundle.getMessage("$textKey.success"), e.project, + NotificationListener { notification, _ -> + notification.expire() + ApplicationManagerEx.getApplicationEx().restart(false) + }, sticky = true) + } + + override fun update(e: AnActionEvent) { + e.presentation.text = P3cBundle.getMessage("$textKey.cur_${p3cConfig.locale}") + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/ToggleProjectInspectionAction.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/ToggleProjectInspectionAction.kt new file mode 100644 index 0000000..1be5d36 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/ToggleProjectInspectionAction.kt @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.action + +import com.alibaba.p3c.idea.compatible.inspection.InspectionProfileService +import com.alibaba.p3c.idea.compatible.inspection.Inspections +import com.alibaba.p3c.idea.config.SmartFoxProjectConfig +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.ServiceManager +import icons.P3cIcons + +/** + * + * Open or close inspections + * @author caikang + * @date 2017/03/14 +4 + */ +class ToggleProjectInspectionAction : AnAction() { + val textKey = "com.alibaba.p3c.idea.action.ToggleProjectInspectionAction.text" + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val smartFoxConfig = ServiceManager.getService(project, SmartFoxProjectConfig::class.java) + val tools = Inspections.aliInspections(project) { + it.tool is AliBaseInspection + } + InspectionProfileService.toggleInspection(project, tools, smartFoxConfig.projectInspectionClosed) + smartFoxConfig.projectInspectionClosed = !smartFoxConfig.projectInspectionClosed + } + + override fun update(e: AnActionEvent?) { + val project = e!!.project ?: return + val smartFoxConfig = ServiceManager.getService(project, SmartFoxProjectConfig::class.java) + e.presentation.text = if (smartFoxConfig.projectInspectionClosed) { + e.presentation.icon = P3cIcons.PROJECT_INSPECTION_ON + P3cBundle.getMessage("$textKey.open") + } else { + e.presentation.icon = P3cIcons.PROJECT_INSPECTION_OFF + P3cBundle.getMessage("$textKey.close") + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/InspectionProfileService.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/InspectionProfileService.kt new file mode 100644 index 0000000..eff1364 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/InspectionProfileService.kt @@ -0,0 +1,116 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.compatible.inspection + +import com.alibaba.smartfox.idea.common.util.PluginVersions +import com.google.common.collect.Sets +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl +import com.intellij.codeInspection.ex.InspectionManagerEx +import com.intellij.codeInspection.ex.InspectionProfileImpl +import com.intellij.codeInspection.ex.InspectionToolWrapper +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.InvalidDataException +import com.intellij.openapi.util.WriteExternalException +import com.intellij.profile.codeInspection.InspectionProjectProfileManager +import com.intellij.psi.PsiElement +import org.jdom.Element +import java.util.LinkedHashSet + +/** + * + * + * @author caikang + * @date 2017/03/01 + */ +object InspectionProfileService { + fun createSimpleProfile(toolWrapperList: List>, + managerEx: InspectionManagerEx, psiElement: PsiElement?): InspectionProfileImpl { + val profile = getProjectInspectionProfile(managerEx.project) + val allWrappers: LinkedHashSet> = Sets.newLinkedHashSet() + allWrappers.addAll(toolWrapperList) + val forCompile = allWrappers + for (toolWrapper in allWrappers) { + profile.collectDependentInspections(toolWrapper, forCompile, managerEx.project) + } + val model = when (PluginVersions.baseVersion) { + in PluginVersions.baseVersion171..Int.MAX_VALUE -> { + val clz = Class.forName("com.intellij.codeInspection.ex.InspectionProfileKt") + val method = clz.methods.first { it.name == "createSimple" } + method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toList()) + as InspectionProfileImpl + } + PluginVersions.baseVersion163 -> { + val method = profile.javaClass.methods.first { + it.name == "createSimple" + } + method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toList()) + as InspectionProfileImpl + } + else -> { + val method = profile.javaClass.methods.first { + it.name == "createSimple" + } + method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toTypedArray()) + as InspectionProfileImpl + } + } + try { + val element = Element("toCopy") + for (wrapper in allWrappers) { + wrapper.tool.writeSettings(element) + val tw = if (psiElement == null) { + model.getInspectionTool(wrapper.shortName, managerEx.project) + } else { + model.getInspectionTool(wrapper.shortName, psiElement) + } + tw!!.tool.readSettings(element) + } + } catch (ignored: WriteExternalException) { + } catch (ignored: InvalidDataException) { + } + return model + } + + fun toggleInspection(project: Project, aliInspections: List>, closed: Boolean) { + val profile = getProjectInspectionProfile(project) + val shortNames = aliInspections.map { + it.tool.shortName + } + profile.removeScopes(shortNames, "AlibabaCodeAnalysis", project) + val method = profile.javaClass.methods.first { + it.name == if (closed) { + "enableToolsByDefault" + } else { + "disableToolByDefault" + } + } + method.invoke(profile, shortNames, project) + profile.profileChanged() + profile.scopesChanged() + } + + fun setExternalProfile(profile: InspectionProfileImpl, + inspectionContext: GlobalInspectionContextImpl) { + val method = inspectionContext.javaClass.methods.first { + it.name == "setExternalProfile" && it.parameterTypes.size == 1 && it.parameterTypes.first().isAssignableFrom(InspectionProfileImpl::class.java) + } + method.invoke(inspectionContext, profile) + } + + fun getProjectInspectionProfile(project: Project): InspectionProfileImpl { + return InspectionProjectProfileManager.getInstance(project).inspectionProfile as InspectionProfileImpl + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/Inspections.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/Inspections.kt new file mode 100644 index 0000000..42d624d --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/Inspections.kt @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.compatible.inspection + +import com.intellij.codeInspection.ex.InspectionProfileImpl +import com.intellij.codeInspection.ex.InspectionToolWrapper +import com.intellij.codeInspection.ex.ScopeToolState +import com.intellij.openapi.project.Project + +/** + * + * + * @author caikang + * @date 2017/03/01 + */ +object Inspections { + fun aliInspections(project: Project, filter: (InspectionToolWrapper<*, *>) -> Boolean): List> { + val profile = InspectionProfileService.getProjectInspectionProfile(project) + return getAllTools(project, profile).filter(filter) + } + + private fun getAllTools(project: Project, profile: InspectionProfileImpl): List> { + val method = InspectionProfileImpl::class.java.methods.first { + it.name == "getAllTools" + } + + val result = if (method.parameterTypes.isNotEmpty()) { + method.invoke(profile, project) + } else { + method.invoke(profile) + } + return (result as List).map { it.tool } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt new file mode 100644 index 0000000..f5ce849 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.component + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.inspection.AliPmdInspectionInvoker +import com.alibaba.p3c.idea.pmd.SourceCodeProcessor +import com.alibaba.smartfox.idea.common.component.AliBaseProjectComponent +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFileAdapter +import com.intellij.openapi.vfs.VirtualFileEvent +import com.intellij.openapi.vfs.VirtualFileListener +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiManager + +/** + * @author caikang + * @date 2016/12/13 + */ +class AliProjectComponent(private val project: Project, + val p3cConfig: P3cConfig) : AliBaseProjectComponent { + private val listener: VirtualFileListener + private val javaExtension = ".java" + private val velocityExtension = ".vm" + + init { + listener = object : VirtualFileAdapter() { + override fun contentsChanged(event: VirtualFileEvent) { + val path = event.file.canonicalPath + if (path == null || !(path.endsWith(javaExtension) || path.endsWith(velocityExtension))) { + return + } + PsiManager.getInstance(project).findFile(event.file) ?: return + if (!p3cConfig.ruleCacheEnable) { + AliPmdInspectionInvoker.refreshFileViolationsCache(event.file) + } + if (!p3cConfig.astCacheEnable) { + SourceCodeProcessor.invalidateCache(path) + } + + } + } + } + + override fun projectOpened() { + VirtualFileManager.getInstance().addVirtualFileListener(listener) + } + + override fun projectClosed() { + VirtualFileManager.getInstance().removeVirtualFileListener(listener) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/CommonSettingsApplicationComponent.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/CommonSettingsApplicationComponent.kt new file mode 100644 index 0000000..13a0a2b --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/CommonSettingsApplicationComponent.kt @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.component + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.util.HighlightInfoTypes +import com.alibaba.p3c.idea.util.HighlightSeverities +import com.alibaba.p3c.pmd.I18nResources +import com.alibaba.smartfox.idea.common.component.AliBaseApplicationComponent +import com.intellij.codeInsight.daemon.impl.SeverityRegistrar +import com.intellij.openapi.actionSystem.ActionManager + +/** + * + * + * @author caikang + * @date 2017/06/19 + */ +class CommonSettingsApplicationComponent(private val p3cConfig: P3cConfig) : AliBaseApplicationComponent { + override fun initComponent() { + SeverityRegistrar.registerStandard(HighlightInfoTypes.BLOCKER, HighlightSeverities.BLOCKER) + SeverityRegistrar.registerStandard(HighlightInfoTypes.CRITICAL, HighlightSeverities.CRITICAL) + SeverityRegistrar.registerStandard(HighlightInfoTypes.MAJOR, HighlightSeverities.MAJOR) + + I18nResources.changeLanguage(p3cConfig.locale) + val analyticsGroup = ActionManager.getInstance().getAction(analyticsGroupId) + analyticsGroup.templatePresentation.text = P3cBundle.getMessage(analyticsGroupText) + } + + companion object { + val analyticsGroupId = "com.alibaba.p3c.analytics.action_group" + val analyticsGroupText = "$analyticsGroupId.text" + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/P3cConfig.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/P3cConfig.kt new file mode 100644 index 0000000..3ea7a61 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/P3cConfig.kt @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.config + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.StoragePathMacros +import com.intellij.util.xmlb.XmlSerializerUtil + +/** + * + * + * @author caikang + * @date 2017/06/19 + */ +@State(name = "P3cConfig", + storages = arrayOf(Storage(file = "${StoragePathMacros.APP_CONFIG}/smartfox/p3c.xml"))) +class P3cConfig : PersistentStateComponent { + var astCacheTime = 1000L + var astCacheEnable = true + + var ruleCacheTime = 1000L + var ruleCacheEnable = false + + var analysisBeforeCheckin = false + + var locale = localeZh + + override fun getState(): P3cConfig { + return this + } + + override fun loadState(state: P3cConfig?) { + if (state == null) { + return + } + XmlSerializerUtil.copyBean(state, this) + } + + companion object { + val localeEn = "en" + val localeZh = "zh" + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/SmartFoxProjectConfig.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/SmartFoxProjectConfig.kt new file mode 100644 index 0000000..2a0d2a0 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/SmartFoxProjectConfig.kt @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.config + +import com.google.common.collect.Sets +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.StoragePathMacros +import com.intellij.util.xmlb.XmlSerializerUtil + +/** + * + * + * @author caikang + * @date 2017/03/01 + */ +@State(name = "SmartFoxProjectConfig", + storages = arrayOf(com.intellij.openapi.components.Storage( + file = "${StoragePathMacros.PROJECT_CONFIG_DIR}/smartfox_info.xml"))) +class SmartFoxProjectConfig : PersistentStateComponent { + var inspectionProfileModifiedSet = Sets.newHashSet()!! + + var projectInspectionClosed = false + + override fun getState(): SmartFoxProjectConfig? { + return this + } + + override fun loadState(state: SmartFoxProjectConfig?) { + if (state == null) { + return + } + XmlSerializerUtil.copyBean(state, this) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/InspectionActionExtensionPoint.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/InspectionActionExtensionPoint.kt new file mode 100644 index 0000000..0a4c457 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/InspectionActionExtensionPoint.kt @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.ep + +import com.alibaba.smartfox.idea.common.util.PluginVersions +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl +import com.intellij.codeInspection.ui.InspectionResultsView +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.project.Project + +/** + * + * + * @author caikang + * @date 2017/06/19 + */ +interface InspectionActionExtensionPoint { + fun doOnInspectionFinished(context: GlobalInspectionContextImpl, projectScopeSelected: Boolean) {} + fun doOnClose(noSuspiciousCodeFound: Boolean, project: Project?) {} + fun doOnView(view: InspectionResultsView) {} + + companion object { + val extension = ExtensionPointName.create( + "${PluginVersions.pluginId.idString}.inspectionAction")!! + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/package-info.java b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/package-info.java new file mode 100644 index 0000000..accb49a --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/package-info.java @@ -0,0 +1,7 @@ +/** + * extension point + * + * @author caikang + * @date 2017/06/19 + */ +package com.alibaba.p3c.idea.ep; \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/i18n/P3cBundle.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/i18n/P3cBundle.kt new file mode 100644 index 0000000..4deb565 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/i18n/P3cBundle.kt @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.i18n + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.pmd.I18nResources +import com.alibaba.smartfox.idea.common.util.getService +import com.intellij.CommonBundle +import java.util.Locale +import java.util.ResourceBundle + +/** + * + * + * @author caikang + * @date 2017/06/20 + */ +object P3cBundle { + val p3cConfig = P3cConfig::class.java.getService() + private val resourceBundle = ResourceBundle.getBundle("messages.P3cBundle", + Locale(p3cConfig.locale), I18nResources.XmlControl()) + + fun getMessage(key: String): String { + return resourceBundle.getString(key).trim() + } + + fun message(key: String, vararg params: Any): String { + return CommonBundle.message(resourceBundle, key, *params).trim() + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliAccessToNonThreadSafeStaticFieldFromInstanceInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliAccessToNonThreadSafeStaticFieldFromInstanceInspection.kt new file mode 100644 index 0000000..7fabb4d --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliAccessToNonThreadSafeStaticFieldFromInstanceInspection.kt @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.siyeh.ig.threading.AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase + +/** + * @author caikang + * @date 2016/12/08 + */ +class AliAccessToNonThreadSafeStaticFieldFromInstanceInspection + : AccessToNonThreadSafeStaticFieldFromInstanceInspectionBase, + AliBaseInspection { + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + init { + nonThreadSafeClasses.clear() + nonThreadSafeClasses.add("java.text.SimpleDateFormat") + } + + override fun ruleName(): String { + return "AvoidCallStaticSimpleDateFormatRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun buildErrorString(vararg infos: Any): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AvoidCallStaticSimpleDateFormatRule.errMsg") + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliArrayNamingShouldHaveBracketInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliArrayNamingShouldHaveBracketInspection.kt new file mode 100644 index 0000000..3fbd2e4 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliArrayNamingShouldHaveBracketInspection.kt @@ -0,0 +1,85 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiParameter +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.style.CStyleArrayDeclarationInspection +import javax.swing.JComponent + +/** + * + * Batch QuickFix Supported + * @author caikang + * @date 2017/02/26 + */ +class AliArrayNamingShouldHaveBracketInspection : CStyleArrayDeclarationInspection, AliBaseInspection { + constructor() + /** + * ForJavassist + */ + constructor(any: Any?) : this() + + override fun ruleName(): String { + return "ArrayNamingShouldHaveBracketRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun getShortName(): String { + return "AliArrayNamingShouldHaveBracket" + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } + + override fun createOptionsPanel(): JComponent? { + return null + } + + override fun buildErrorString(vararg infos: Any?): String { + val info = infos[0] + if (info is PsiMethod) { + return displayName + } + val choice = if (info is PsiField) 1 else if (info is PsiParameter) 2 else 3 + return P3cBundle.message("com.alibaba.p3c.idea.inspection.rule.ArrayNamingShouldHaveBracketRule.errMsg", choice) + } + + override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + val fix = super.buildFix(*infos) ?: return null + return DecorateInspectionGadgetsFix(fix, + P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.ArrayNamingShouldHaveBracketRule")) + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + return buildFix(psiElement) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliBaseInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliBaseInspection.kt new file mode 100644 index 0000000..a674ee3 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliBaseInspection.kt @@ -0,0 +1,82 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile + +/** + * @author caikang + * @date 2016/12/08 + */ +interface AliBaseInspection { + + /** + * ruleName + + * @return ruleName + */ + fun ruleName(): String + + /** + * display info for inspection + + * @return display + */ + fun getDisplayName(): String + + /** + * group display info for inspection + + * @return group display + */ + fun getGroupDisplayName(): String + + /** + * inspection enable by default + + * @return true -> enable + */ + fun isEnabledByDefault(): Boolean + + /** + * default inspection level + + * @return level + */ + fun getDefaultLevel(): HighlightDisplayLevel + + /** + * inspection short name + + * @return shor name + */ + fun getShortName(): String + + fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? = null + + fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, + start: Int, end: Int): PsiElement { + return psiFile.findElementAt(start)!! + } + + companion object { + val GROUP_NAME = "Ali-Check" + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliControlFlowStatementWithoutBracesInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliControlFlowStatementWithoutBracesInspection.kt new file mode 100644 index 0000000..8a5d7ec --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliControlFlowStatementWithoutBracesInspection.kt @@ -0,0 +1,68 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.style.ControlFlowStatementWithoutBracesInspection + +/** + * Batch QuickFix Supported + * @author caikang + * @date 2016/12/15 + */ +class AliControlFlowStatementWithoutBracesInspection + : ControlFlowStatementWithoutBracesInspection, + AliBaseInspection { + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + override fun ruleName(): String { + return "NeedBraceRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun buildErrorString(vararg infos: Any): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.NeedBraceRule.errMsg") + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } + + override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + val fix = super.buildFix(*infos) ?: return null + return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.NeedBraceRule")) + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + return buildFix(psiElement) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliEqualsAvoidNullInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliEqualsAvoidNullInspection.kt new file mode 100644 index 0000000..2e7e5f5 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliEqualsAvoidNullInspection.kt @@ -0,0 +1,133 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiExpression +import com.intellij.psi.PsiField +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiLiteralExpression +import com.intellij.psi.PsiMethodCallExpression +import com.intellij.psi.PsiReferenceExpression +import com.siyeh.HardcodedMethodConstants +import com.siyeh.ig.BaseInspectionVisitor +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.psiutils.TypeUtils +import com.siyeh.ig.style.LiteralAsArgToStringEqualsInspection +import org.jetbrains.annotations.NonNls + +/** + * + * Batch QuickFix Supported + * @author caikang + * @date 2017/02/27 + */ +class AliEqualsAvoidNullInspection : LiteralAsArgToStringEqualsInspection, AliBaseInspection { + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + override fun ruleName(): String { + return "EqualsAvoidNullRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun buildErrorString(vararg infos: Any?): String { + val methodName = infos[0] as String + return String.format(P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AliEqualsAvoidNull.errMsg"), + methodName) + } + + override fun getShortName(): String { + return "AliEqualsAvoidNull" + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } + + override fun buildVisitor(): BaseInspectionVisitor { + return LiteralAsArgToEqualsVisitor() + } + + private class LiteralAsArgToEqualsVisitor : BaseInspectionVisitor() { + + override fun visitMethodCallExpression( + expression: PsiMethodCallExpression) { + super.visitMethodCallExpression(expression) + val methodExpression = expression.methodExpression + @NonNls val methodName = methodExpression.referenceName + if (HardcodedMethodConstants.EQUALS != methodName && HardcodedMethodConstants.EQUALS_IGNORE_CASE != methodName) { + return + } + val argList = expression.argumentList + val args = argList.expressions + if (args.size != 1) { + return + } + val argument = args[0] + val argumentType = argument.type ?: return + if (argument !is PsiLiteralExpression && !isConstantField(argument)) { + return + } + if (!TypeUtils.isJavaLangString(argumentType)) { + return + } + val target = methodExpression.qualifierExpression + if (target is PsiLiteralExpression || isConstantField(argument)) { + return + } + registerError(argument, methodName) + } + + private fun isConstantField(argument: PsiExpression): Boolean { + if (argument !is PsiReferenceExpression) { + return false + } + val psiField = argument.resolve() as? PsiField ?: return false + val modifierList = psiField.modifierList ?: return false + return modifierList.hasModifierProperty("final") && modifierList.hasModifierProperty("static") + } + } + + override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + val fix = super.buildFix(*infos) ?: return null + return DecorateInspectionGadgetsFix(fix, + P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.AliEqualsAvoidNull")) + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + return buildFix(psiElement) + } + + override fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, start: Int, end: Int): PsiElement { + return psiFile.findElementAt(start)!!.parent.parent + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt new file mode 100644 index 0000000..51549ec --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt @@ -0,0 +1,192 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.inspection.standalone.AliAccessStaticViaInstanceInspection +import com.alibaba.p3c.idea.inspection.standalone.AliDeprecationInspection +import com.alibaba.p3c.idea.inspection.standalone.AliMissingOverrideAnnotationInspection +import com.alibaba.p3c.idea.inspection.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsInspection +import com.alibaba.p3c.pmd.I18nResources +import com.alibaba.smartfox.idea.common.util.getService +import com.beust.jcommander.internal.Lists +import com.beust.jcommander.internal.Maps +import com.intellij.codeInspection.InspectionToolProvider +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.psi.PsiCompiledFile +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiImportList +import com.intellij.psi.PsiJavaFile +import javassist.CannotCompileException +import javassist.ClassClassPath +import javassist.ClassPool +import javassist.CtField +import javassist.NotFoundException +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RuleSetFactory +import net.sourceforge.pmd.RuleSetNotFoundException +import javax.annotation.Generated + +/** + * @author caikang + * @date 2016/12/16 + */ +class AliLocalInspectionToolProvider : InspectionToolProvider { + + override fun getInspectionClasses(): Array> { + return CLASS_LIST.toTypedArray() + } + + interface ShouldInspectChecker { + /** + * check inspect whether or not + + * @param file file to inspect + * @return true or false + */ + fun shouldInspect(file: PsiFile): Boolean + } + + class RuleInfo(var rule: Rule, var shouldInspectChecker: ShouldInspectChecker) + + companion object { + val ruleInfoMap: MutableMap = Maps.newHashMap() + private val LOGGER = Logger.getInstance(AliLocalInspectionToolProvider::class.java) + val ruleNames: MutableList = Lists.newArrayList()!! + private val CLASS_LIST = Lists.newArrayList>() + private val nativeInspectionToolClass = arrayListOf>( + AliMissingOverrideAnnotationInspection::class.java, + AliAccessStaticViaInstanceInspection::class.java, + AliDeprecationInspection::class.java, + MapOrSetKeyShouldOverrideHashCodeEqualsInspection::class.java, + AliAccessToNonThreadSafeStaticFieldFromInstanceInspection::class.java, + AliArrayNamingShouldHaveBracketInspection::class.java, + AliControlFlowStatementWithoutBracesInspection::class.java, + AliEqualsAvoidNullInspection::class.java, + AliLongLiteralsEndingWithLowercaseLInspection::class.java, + AliWrapperTypeEqualityInspection::class.java + ) + val javaShouldInspectChecker = object : ShouldInspectChecker { + override fun shouldInspect(file: PsiFile): Boolean { + val basicInspect = file is PsiJavaFile && file !is PsiCompiledFile + if (!basicInspect) { + return false + } + + if (!validScope(file)) { + return false + } + + val importList = file.children.firstOrNull { + it is PsiImportList + } as? PsiImportList ?: return true + + return !importList.allImportStatements.any { + it.text.contains(Generated::class.java.name) + } + } + + private fun validScope(file: PsiFile): Boolean { + val virtualFile = file.virtualFile + val index = ProjectRootManager.getInstance(file.project).fileIndex + return index.isInSource(virtualFile) + && !index.isInTestSourceContent(virtualFile) + && !index.isInLibraryClasses(virtualFile) + && !index.isInLibrarySource(virtualFile) + } + } + + init { + I18nResources.changeLanguage(P3cConfig::class.java.getService().locale) + Thread.currentThread().contextClassLoader = AliLocalInspectionToolProvider::class.java.classLoader + initPmdInspection() + initNativeInspection() + } + + private fun initNativeInspection() { + val pool = ClassPool.getDefault() + pool.insertClassPath(ClassClassPath(DelegateLocalInspectionTool::class.java)) + nativeInspectionToolClass.forEach { + pool.insertClassPath(ClassClassPath(it)) + val cc = pool.get(DelegateLocalInspectionTool::class.java.name) + cc.name = "Delegate${it.simpleName}" + val ctField = cc.getField("forJavassist") + cc.removeField(ctField) + val itClass = pool.get(it.name) + val toolClass = pool.get(LocalInspectionTool::class.java.name) + val newField = CtField(toolClass, "forJavassist", cc) + cc.addField(newField, CtField.Initializer.byNew(itClass)) + CLASS_LIST.add(cc.toClass()) + } + } + + private fun initPmdInspection() { + for (ri in newRuleInfos()) { + this.ruleNames.add(ri.rule.name) + ruleInfoMap.put(ri.rule.name, ri) + } + val pool = ClassPool.getDefault() + pool.insertClassPath(ClassClassPath(DelegatePmdInspection::class.java)) + try { + for (ruleInfo in ruleInfoMap.values) { + val cc = pool.get(DelegatePmdInspection::class.java.name) + cc.name = ruleInfo.rule.name + "Inspection" + val ctField = cc.getField("ruleName") + cc.removeField(ctField) + val value = "\"" + ruleInfo.rule.name + "\"" + val newField = CtField.make("private String ruleName = $value;", cc) + cc.addField(newField, value) + CLASS_LIST.add(cc.toClass()) + } + + } catch (e: NotFoundException) { + LOGGER.error(e) + } catch (e: CannotCompileException) { + LOGGER.error(e) + } + } + + private fun newRuleInfos(): List { + val result = Lists.newArrayList() + result.addAll(processForRuleSet("java/ali-pmd", javaShouldInspectChecker)) + result.addAll(processForRuleSet("vm/ali-other", object : ShouldInspectChecker { + override fun shouldInspect(file: PsiFile): Boolean { + val virtualFile = file.virtualFile ?: return false + val path = virtualFile.canonicalPath + return path != null && path.endsWith(".vm") + } + })) + return result + } + + private fun processForRuleSet(ruleSetName: String, shouldInspectChecker: ShouldInspectChecker): List { + val factory = RuleSetFactory() + val result = Lists.newArrayList() + try { + val ruleSet = factory.createRuleSet(ruleSetName.replace("/", "-")) + ruleSet.rules.mapTo(result) { + RuleInfo(it, shouldInspectChecker) + } + } catch (e: RuleSetNotFoundException) { + LOGGER.error(String.format("rule set %s not found for", ruleSetName)) + } + + return result + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLongLiteralsEndingWithLowercaseLInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLongLiteralsEndingWithLowercaseLInspection.kt new file mode 100644 index 0000000..c1fa746 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLongLiteralsEndingWithLowercaseLInspection.kt @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.numeric.LongLiteralsEndingWithLowercaseLInspection + +/** + * + * Batch QuickFix Supported + * @author caikang + * @date 2017/01/20 + */ +class AliLongLiteralsEndingWithLowercaseLInspection : LongLiteralsEndingWithLowercaseLInspection, AliBaseInspection { + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + override fun ruleName(): String { + return "UpperEllRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun buildErrorString(vararg infos: Any?): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AliLongLiteralsEndingWithLowercaseL.errMsg") + } + + override fun getShortName(): String { + return "AliLongLiteralsEndingWithLowercaseL" + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } + + override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + val fix = super.buildFix(*infos) ?: return null + return DecorateInspectionGadgetsFix(fix, + P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.AliLongLiteralsEndingWithLowercaseL")) + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + return buildFix(psiElement) + } + + override fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, start: Int, end: Int): PsiElement { + return psiFile.findElementAt(start)!!.parent + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspection.kt new file mode 100644 index 0000000..1f170e4 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspection.kt @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.inspection.AliLocalInspectionToolProvider.ShouldInspectChecker +import com.alibaba.p3c.idea.util.QuickFixes +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import net.sourceforge.pmd.Rule +import org.jetbrains.annotations.Nls + +/** + * @author caikang + * @date 2016/12/16 + */ +class AliPmdInspection(private val ruleName: String) + : LocalInspectionTool(), + AliBaseInspection, + PmdRuleInspectionIdentify { + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + return QuickFixes.getQuickFix(ruleName, isOnTheFly) + } + + private val staticDescription: String = RuleInspectionUtils.getRuleStaticDescription(ruleName) + + private val displayName: String + + private val shouldInspectChecker: ShouldInspectChecker + + private val defaultLevel: HighlightDisplayLevel + + private val rule: Rule + + init { + val ruleInfo = AliLocalInspectionToolProvider.ruleInfoMap[ruleName]!! + shouldInspectChecker = ruleInfo.shouldInspectChecker + rule = ruleInfo.rule + displayName = rule.message + defaultLevel = RuleInspectionUtils.getHighlightDisplayLevel(rule.priority) + } + + override fun runForWholeFile(): Boolean { + return true + } + + override fun checkFile(file: PsiFile, manager: InspectionManager, + isOnTheFly: Boolean): Array? { + if (!shouldInspectChecker.shouldInspect(file)) { + return null + } + return AliPmdInspectionInvoker.invokeInspection(file, manager, rule, isOnTheFly) + } + + override fun getStaticDescription(): String? { + return staticDescription + } + + override fun ruleName(): String { + return ruleName + } + + @Nls + override fun getDisplayName(): String { + return displayName + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return defaultLevel + } + + @Nls + override fun getGroupDisplayName(): String { + return AliBaseInspection.GROUP_NAME + } + + override fun isEnabledByDefault(): Boolean { + return true + } + + override fun isSuppressedFor(element: PsiElement): Boolean { + return false + } + + override fun getShortName(): String { + + var shortName = "Alibaba" + ruleName + val index = shortName.lastIndexOf("Rule") + if (index > com.alibaba.p3c.idea.NumberConstants.INDEX_0) { + shortName = shortName.substring(com.alibaba.p3c.idea.NumberConstants.INDEX_0, index) + } + return shortName + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt new file mode 100644 index 0000000..1875742 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt @@ -0,0 +1,132 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.pmd.AliPmdProcessor +import com.alibaba.p3c.idea.util.DocumentUtils.calculateRealOffset +import com.alibaba.p3c.idea.util.ProblemsUtils +import com.beust.jcommander.internal.Lists +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RuleViolation +import java.util.concurrent.TimeUnit + +/** + * @author caikang + * @date 2016/12/13 + */ +class AliPmdInspectionInvoker(private val psiFile: PsiFile, + private val manager: InspectionManager, + private val rule: Rule) { + val logger = Logger.getInstance(javaClass) + + private var violations: List = emptyList() + + fun doInvoke() { + Thread.currentThread().contextClassLoader = javaClass.classLoader + val processor = AliPmdProcessor(rule) + val start = System.currentTimeMillis() + violations = processor.processFile(psiFile) + logger.info("elapsed ${System.currentTimeMillis() - start}ms to" + + " to apply rule ${rule.name} for file ${psiFile.virtualFile.canonicalPath}") + } + + fun getRuleProblems(isOnTheFly: Boolean): Array? { + if (violations.isEmpty()) { + return null + } + val problemDescriptors = Lists.newArrayList(violations.size) + for (rv in violations) { + val virtualFile = LocalFileSystem.getInstance().findFileByPath(rv.filename) ?: continue + val psiFile = PsiManager.getInstance(manager.project).findFile(virtualFile) ?: continue + val document = FileDocumentManager.getInstance().getDocument(virtualFile) ?: continue + val offset = calculateRealOffset(document, rv.beginLine, rv.beginColumn) + val endOffset = calculateRealOffset(document, rv.endLine, rv.endColumn) + val errorMessage = if (isOnTheFly) { + rv.description + } else { + "${rv.description} (line ${rv.beginLine})" + } + val problemDescriptor = ProblemsUtils.createProblemDescriptorForPmdRule(psiFile, manager, + isOnTheFly, rv.rule.name, errorMessage, offset, endOffset, rv.beginLine) ?: continue + problemDescriptors.add(problemDescriptor) + } + return problemDescriptors.toTypedArray() + } + + companion object { + private lateinit var invokers: Cache + + val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!! + + init { + reInitInvokers(smartFoxConfig.ruleCacheTime) + } + + fun invokeInspection(psiFile: PsiFile?, manager: InspectionManager, rule: Rule, + isOnTheFly: Boolean): Array? { + if (psiFile == null) { + return null + } + val virtualFile = psiFile.virtualFile ?: return null + if (!smartFoxConfig.ruleCacheEnable) { + val invoker = AliPmdInspectionInvoker(psiFile, manager, rule) + invoker.doInvoke() + return invoker.getRuleProblems(isOnTheFly) + } + var invoker = invokers.getIfPresent(FileRule(virtualFile.canonicalPath!!, rule.name)) + if (invoker == null) { + synchronized(virtualFile) { + invoker = invokers.getIfPresent(virtualFile.canonicalPath) + if (invoker == null) { + invoker = AliPmdInspectionInvoker(psiFile, manager, rule) + invoker!!.doInvoke() + invokers.put(FileRule(virtualFile.canonicalPath!!, rule.name), invoker) + } + } + } + return invoker!!.getRuleProblems(isOnTheFly) + } + + private fun doInvokeIfPresent(filePath: String, rule: String) { + invokers.getIfPresent(FileRule(filePath, rule))?.doInvoke() + } + + fun refreshFileViolationsCache(file: VirtualFile) { + AliLocalInspectionToolProvider.ruleNames.forEach { + doInvokeIfPresent(file.canonicalPath!!, it) + } + } + + fun reInitInvokers(expireTime: Long) { + invokers = CacheBuilder.newBuilder().maximumSize(500).expireAfterWrite(expireTime, + TimeUnit.MILLISECONDS).build()!! + } + } +} + +data class FileRule(val filePath: String, val ruleName: String) diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliWrapperTypeEqualityInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliWrapperTypeEqualityInspection.kt new file mode 100644 index 0000000..cb6b28a --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliWrapperTypeEqualityInspection.kt @@ -0,0 +1,182 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.CommonClassNames +import com.intellij.psi.JavaTokenType +import com.intellij.psi.PsiArrayType +import com.intellij.psi.PsiBinaryExpression +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiExpression +import com.intellij.util.IncorrectOperationException +import com.siyeh.ig.BaseInspection +import com.siyeh.ig.BaseInspectionVisitor +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.PsiReplacementUtil +import com.siyeh.ig.fixes.EqualityToEqualsFix +import com.siyeh.ig.psiutils.ComparisonUtils +import com.siyeh.ig.psiutils.TypeUtils +import org.jetbrains.annotations.NonNls + +/** + * + * Batch QuickFix Supported + * @author caikang + * @date 2017/02/27 + */ +class AliWrapperTypeEqualityInspection : BaseInspection, AliBaseInspection { + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + val familyName = "$replaceWith equals" + + override fun buildErrorString(vararg infos: Any?): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.WrapperTypeEqualityRule.errMsg") + } + + override fun buildVisitor(): BaseInspectionVisitor { + return ObjectComparisonVisitor() + } + + override fun ruleName(): String { + return "WrapperTypeEqualityRule" + } + + override fun getDisplayName(): String { + return RuleInspectionUtils.getRuleMessage(ruleName()) + } + + override fun getShortName(): String { + return "AliWrapperTypeEquality" + } + + override fun getStaticDescription(): String? { + return RuleInspectionUtils.getRuleStaticDescription(ruleName()) + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) + } + + public override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + if (infos.isEmpty()) { + return DecorateInspectionGadgetsFix(EqualityToEqualsFix(), familyName) + } + val type = infos[0] as PsiArrayType + val componentType = type.componentType + val fix = ArrayEqualityFix(componentType is PsiArrayType) + return DecorateInspectionGadgetsFix(fix, fix.name, familyName) + } + + private inner class ObjectComparisonVisitor : BaseInspectionVisitor() { + override fun visitBinaryExpression(expression: PsiBinaryExpression) { + if (!ComparisonUtils.isEqualityComparison(expression)) { + return + } + checkForWrapper(expression) + } + + private fun checkForWrapper(expression: PsiBinaryExpression) { + val rhs = expression.rOperand ?: return + val lhs = expression.lOperand + if (!isWrapperType(lhs) || !isWrapperType(rhs)) { + return + } + registerError(expression.operationSign) + } + + private fun isWrapperType(expression: PsiExpression): Boolean { + if (hasNumberType(expression)) { + return true + } + return TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_BOOLEAN) + || TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_CHARACTER) + } + + + private fun hasNumberType(expression: PsiExpression): Boolean { + return TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_NUMBER) + } + /** + * checkForNumber end + */ + + } + + private class ArrayEqualityFix(private val deepEquals: Boolean) : InspectionGadgetsFix() { + + override fun getName(): String { + if (deepEquals) { + return "$replaceWith 'Arrays.deepEquals()'" + } else { + return "$replaceWith 'Arrays.equals()'" + } + } + + override fun getFamilyName(): String { + return familyName + } + + @Throws(IncorrectOperationException::class) + override fun doFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement + val parent = element.parent as? PsiBinaryExpression ?: return + val tokenType = parent.operationTokenType + @NonNls val newExpressionText = StringBuilder() + if (JavaTokenType.NE == tokenType) { + newExpressionText.append('!') + } else if (JavaTokenType.EQEQ != tokenType) { + return + } + if (deepEquals) { + newExpressionText.append("java.util.Arrays.deepEquals(") + } else { + newExpressionText.append("java.util.Arrays.equals(") + } + newExpressionText.append(parent.lOperand.text) + newExpressionText.append(',') + val rhs = parent.rOperand ?: return + newExpressionText.append(rhs.text) + newExpressionText.append(')') + PsiReplacementUtil.replaceExpressionAndShorten(parent, + newExpressionText.toString()) + } + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { + val expression = psiElement.parent as? PsiBinaryExpression ?: return null + val rhs = expression.rOperand ?: return null + val lhs = expression.lOperand + val lhsType = lhs.type + if (lhsType !is PsiArrayType || rhs.type !is PsiArrayType) { + return buildFix() + } + return buildFix(lhsType) + } + + companion object { + val replaceWith = P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.replace.with") + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegateLocalInspectionTool.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegateLocalInspectionTool.kt new file mode 100644 index 0000000..b8955c6 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegateLocalInspectionTool.kt @@ -0,0 +1,94 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.Nls + +/** + * + * @author caikang + * @date 2017/07/19 + */ +class DelegateLocalInspectionTool : LocalInspectionTool(), AliBaseInspection { + + private val forJavassist: LocalInspectionTool? = null + + private val localInspectionTool: LocalInspectionTool + + init { + localInspectionTool = forJavassist ?: throw IllegalStateException() + } + + override fun runForWholeFile(): Boolean { + return localInspectionTool.runForWholeFile() + } + + override fun checkFile(file: PsiFile, manager: InspectionManager, + isOnTheFly: Boolean): Array? { + return localInspectionTool.checkFile(file, manager, isOnTheFly) + } + + override fun getStaticDescription(): String? { + return localInspectionTool.staticDescription + } + + override fun ruleName(): String { + return (localInspectionTool as AliBaseInspection).ruleName() + } + + @Nls + override fun getDisplayName(): String { + return localInspectionTool.displayName + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return localInspectionTool.defaultLevel + } + + @Nls + override fun getGroupDisplayName(): String { + return AliBaseInspection.GROUP_NAME + } + + override fun isEnabledByDefault(): Boolean { + return true + } + + override fun getShortName(): String { + return localInspectionTool.shortName + } + + override fun isSuppressedFor(element: PsiElement): Boolean { + return false + } + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, + session: LocalInspectionToolSession): PsiElementVisitor { + if (!AliLocalInspectionToolProvider.javaShouldInspectChecker.shouldInspect(holder.file)) { + return PsiElementVisitor.EMPTY_VISITOR + } + return localInspectionTool.buildVisitor(holder, isOnTheFly, session) + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegatePmdInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegatePmdInspection.kt new file mode 100644 index 0000000..fa2b681 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegatePmdInspection.kt @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile + +import org.jetbrains.annotations.Nls + +/** + * @author caikang + * @date 2017/02/28 + */ +class DelegatePmdInspection : LocalInspectionTool(), AliBaseInspection, PmdRuleInspectionIdentify { + + private val ruleName: String? = null + + private val aliPmdInspection: AliPmdInspection + + init { + aliPmdInspection = AliPmdInspection(ruleName!!) + } + + override fun runForWholeFile(): Boolean { + return aliPmdInspection.runForWholeFile() + } + + override fun checkFile(file: PsiFile, manager: InspectionManager, + isOnTheFly: Boolean): Array? { + return aliPmdInspection.checkFile(file, manager, isOnTheFly) + } + + override fun getStaticDescription(): String? { + return aliPmdInspection.staticDescription + } + + override fun ruleName(): String { + return ruleName!! + } + + @Nls + override fun getDisplayName(): String { + return aliPmdInspection.displayName + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return aliPmdInspection.defaultLevel + } + + @Nls + override fun getGroupDisplayName(): String { + return aliPmdInspection.groupDisplayName + } + + override fun isEnabledByDefault(): Boolean { + return aliPmdInspection.isEnabledByDefault + } + + override fun getShortName(): String { + return aliPmdInspection.shortName + } + + override fun isSuppressedFor(element: PsiElement): Boolean { + return false + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/PmdRuleInspectionIdentify.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/PmdRuleInspectionIdentify.kt new file mode 100644 index 0000000..b84b5bd --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/PmdRuleInspectionIdentify.kt @@ -0,0 +1,26 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +/** + * + * + * @author caikang + * @date 2017/03/16 +6 + */ +interface PmdRuleInspectionIdentify { +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/RuleInspectionUtils.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/RuleInspectionUtils.kt new file mode 100644 index 0000000..fce0372 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/RuleInspectionUtils.kt @@ -0,0 +1,197 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.util.HighlightDisplayLevels +import com.alibaba.p3c.pmd.I18nResources +import com.alibaba.smartfox.idea.common.util.getService +import com.google.common.base.Joiner +import com.google.common.collect.ImmutableMap +import com.google.common.collect.Lists +import com.google.common.collect.Maps +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.io.URLUtil +import freemarker.template.Configuration +import freemarker.template.TemplateException +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RulePriority +import net.sourceforge.pmd.RuleSetFactory +import net.sourceforge.pmd.RuleSetNotFoundException +import org.apache.commons.lang3.StringUtils +import java.io.File +import java.io.IOException +import java.io.StringWriter +import java.net.URL +import java.net.URLDecoder +import java.nio.charset.StandardCharsets +import java.util.jar.JarFile +import java.util.regex.Pattern + +/** + * @author caikang + * @date 2016/12/16 + */ +object RuleInspectionUtils { + + private val logger = Logger.getInstance(RuleInspectionUtils::class.java) + private val ruleSetFilePattern = Pattern.compile("(java|vm)/ali-.*?\\.xml") + private val staticDescriptionTemplate = run { + val cfg = Configuration(Configuration.VERSION_2_3_25) + cfg.setClassForTemplateLoading(RuleInspectionUtils::class.java, "/tpl") + cfg.defaultEncoding = "UTF-8" + cfg.getTemplate("StaticDescriptionTemplate.ftl") + } + + private val ruleSetsPrefix = "rulesets/" + + private val ruleStaticDescriptions: Map + private val ruleMessages: Map + private val displayLevelMap: Map + + init { + I18nResources.changeLanguage(P3cConfig::class.java.getService().locale) + val builder = ImmutableMap.builder() + val messageBuilder = ImmutableMap.builder() + val displayLevelBuilder = ImmutableMap.builder() + val rules = loadAllAlibabaRule() + for (rule in rules) { + builder.put(rule.name, parseStaticDescription(rule)) + messageBuilder.put(rule.name, rule.message) + displayLevelBuilder.put(rule.name, getHighlightDisplayLevel(rule.priority)) + } + ruleStaticDescriptions = builder.build() + ruleMessages = messageBuilder.build() + displayLevelMap = displayLevelBuilder.build() + } + + fun getRuleStaticDescription(ruleName: String): String { + return ruleStaticDescriptions[ruleName]!! + } + + fun getHighlightDisplayLevel(ruleName: String): HighlightDisplayLevel { + val level = displayLevelMap[ruleName] + + return level ?: HighlightDisplayLevel.WEAK_WARNING + } + + fun getHighlightDisplayLevel(rulePriority: RulePriority): HighlightDisplayLevel { + when (rulePriority) { + RulePriority.HIGH -> return HighlightDisplayLevels.BLOCKER + RulePriority.MEDIUM_HIGH -> return HighlightDisplayLevels.CRITICAL + else -> return HighlightDisplayLevels.MAJOR + } + } + + fun getRuleMessage(ruleName: String): String { + return ruleMessages[ruleName]!! + } + + private fun parseStaticDescription(rule: Rule): String { + val writer = StringWriter() + try { + val map = Maps.newHashMap() + map.put("message", StringUtils.trimToEmpty(rule.message)) + map.put("description", StringUtils.trimToEmpty(rule.description)) + val examples = rule.examples.map { + it?.trim { + c -> + c == '\n' + } + } + map.put("examples", examples) + staticDescriptionTemplate.process(map, writer) + } catch (e: TemplateException) { + logger.error(e) + } catch (e: IOException) { + logger.error(e) + } + return writer.toString() + } + + private fun loadAllAlibabaRule(): List { + try { + Thread.currentThread().contextClassLoader = RuleInspectionUtils::class.java.classLoader + val ruleSetConfigs = findRuleSetConfigs() + val ruleSetFactory = RuleSetFactory() + val ruleSets = ruleSetFactory.createRuleSets( + Joiner.on(",").join(ruleSetConfigs).replace("/".toRegex(), "-")) + val map = Maps.newHashMap() + ruleSets.allRuleSets + .asSequence() + .flatMap { it.rules.asSequence() } + .forEach { map.put(it.name, it) } + return Lists.newArrayList(map.values) + } catch (e: IOException) { + logger.warn("no available alibaba rules") + return emptyList() + } catch (e: RuleSetNotFoundException) { + logger.error("rule sets not found", e) + return emptyList() + } + + } + + @Throws(IOException::class) + private fun findRuleSetConfigs(): List { + val ruleSets = Lists.newArrayList() + val enumeration = RuleInspectionUtils::class.java.classLoader.getResources(ruleSetsPrefix) + while (enumeration.hasMoreElements()) { + val url = enumeration.nextElement() + if (URLUtil.JAR_PROTOCOL == url.protocol) { + findRuleSetsFromJar(ruleSets, url) + } else if (URLUtil.FILE_PROTOCOL == url.protocol) { + findRuleSetsFromDirectory(ruleSets, url) + } + } + return ruleSets + } + + @Throws(IOException::class) + private fun findRuleSetsFromDirectory(ruleSets: MutableList, url: URL) { + val file = File(url.path) + if (file.exists() && file.isDirectory) { + val files = Lists.newArrayList() + FileUtil.collectMatchedFiles(file, ruleSetFilePattern, files) + files.mapTo(ruleSets) { it.canonicalPath.replace(url.path, "").replace(".xml", "") } + } + } + + @Throws(IOException::class) + private fun findRuleSetsFromJar(ruleSets: MutableList, url: URL) { + logger.info("start to find rule sets from jar " + url) + var path = URLDecoder.decode(url.path, StandardCharsets.UTF_8.name()) + val index = path.lastIndexOf(URLUtil.JAR_SEPARATOR) + if (index > com.alibaba.p3c.idea.NumberConstants.INDEX_0) { + path = path.substring("file:".length, index) + } + val jarFile = JarFile(path) + logger.info("create jarFile for path " + path) + val jarEntries = jarFile.entries() + while (jarEntries.hasMoreElements()) { + val jarEntry = jarEntries.nextElement() + val subPath = jarEntry.name.replace(ruleSetsPrefix, "") + if (ruleSetFilePattern.matcher(subPath).find()) { + val resultPath = subPath.replace(".xml", "") + logger.info("get result rule set " + resultPath) + ruleSets.add(resultPath) + } + } + logger.info("find rule sets from jar $url finished") + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliAccessStaticViaInstanceInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliAccessStaticViaInstanceInspection.kt new file mode 100644 index 0000000..ba5001e --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliAccessStaticViaInstanceInspection.kt @@ -0,0 +1,140 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection.standalone + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.alibaba.p3c.idea.util.HighlightDisplayLevels +import com.alibaba.smartfox.idea.common.util.getService +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil +import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil +import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil +import com.intellij.codeInsight.daemon.impl.quickfix.AccessStaticViaInstanceFix +import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance +import com.intellij.psi.JavaElementVisitor +import com.intellij.psi.JavaResolveResult +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiPackage +import com.intellij.psi.PsiReferenceExpression +import com.intellij.psi.PsiSubstitutor +import java.util.ArrayList + +/** + * @author caikang + * @date 2016/12/08 + */ +class AliAccessStaticViaInstanceInspection : AccessStaticViaInstance, AliBaseInspection { + val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliAccessStaticViaInstanceInspection" + + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + override fun getDisplayName(): String { + return P3cBundle.getMessage("$messageKey.message") + } + + override fun getStaticDescription(): String? { + return P3cBundle.getMessage("$messageKey.desc") + } + + override fun ruleName(): String { + return "AvoidAccessStaticViaInstanceRule" + } + + override fun getShortName(): String { + return "AliAccessStaticViaInstance" + } + + override fun createAccessStaticViaInstanceFix(expr: PsiReferenceExpression, + onTheFly: Boolean, + result: JavaResolveResult): AccessStaticViaInstanceFix { + return object : AccessStaticViaInstanceFix(expr, result, onTheFly) { + val fixKey = "com.alibaba.p3c.idea.quickfix.standalone.AliAccessStaticViaInstanceInspection" + internal val text = calcText(result.element as PsiMember, result.substitutor) + + override fun getText(): String { + return text + } + + private fun calcText(member: PsiMember, substitutor: PsiSubstitutor): String { + val aClass = member.containingClass ?: return "" + val p3cConfig = P3cConfig::class.java.getService() + return when (p3cConfig.locale) { + P3cConfig.localeZh -> String.format(P3cBundle.getMessage(fixKey), + HighlightUtil.formatClass(aClass, false), + HighlightUtil.formatClass(aClass), HighlightMessageUtil.getSymbolName(member, substitutor)) + else -> String.format(P3cBundle.getMessage(fixKey), HighlightUtil.formatClass(aClass), + HighlightMessageUtil.getSymbolName(member, substitutor), + HighlightUtil.formatClass(aClass, false)) + } + } + } + } + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : JavaElementVisitor() { + override fun visitReferenceExpression(expression: PsiReferenceExpression) { + checkAccessStaticMemberViaInstanceReference(expression, holder, isOnTheFly) + } + } + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return HighlightDisplayLevels.BLOCKER + } + + private fun checkAccessStaticMemberViaInstanceReference(expr: PsiReferenceExpression, holder: ProblemsHolder, + onTheFly: Boolean) { + val result = expr.advancedResolve(false) + val resolved = result.element as? PsiMember ?: return + + val qualifierExpression = expr.qualifierExpression ?: return + + if (qualifierExpression is PsiReferenceExpression) { + val qualifierResolved = qualifierExpression.resolve() + if (qualifierResolved is PsiClass || qualifierResolved is PsiPackage) { + return + } + } + if (!resolved.hasModifierProperty(PsiModifier.STATIC)) { + return + } + + val description = String.format(P3cBundle.getMessage( + "$messageKey.errMsg"), + "${JavaHighlightUtil.formatType(qualifierExpression.type)}.${HighlightMessageUtil.getSymbolName( + resolved, result.substitutor)}") + if (!onTheFly) { + if (RemoveUnusedVariableUtil.checkSideEffects(qualifierExpression, null, ArrayList())) { + holder.registerProblem(expr, description) + return + } + } + holder.registerProblem(expr, description, createAccessStaticViaInstanceFix(expr, onTheFly, result)) + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliDeprecationInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliDeprecationInspection.kt new file mode 100644 index 0000000..3016ab0 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliDeprecationInspection.kt @@ -0,0 +1,132 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection.standalone + +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.alibaba.p3c.idea.util.HighlightDisplayLevels +import com.alibaba.smartfox.idea.common.util.getService +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.codeInspection.deprecation.DeprecationInspection +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiReference +import org.jetbrains.annotations.Nls +import javax.swing.JComponent + +/** + * @author caikang + * @date 2016/12/08 + */ +class AliDeprecationInspection : DeprecationInspection, AliBaseInspection { + val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliDeprecationInspection" + + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + init { + IGNORE_INSIDE_DEPRECATED = false + IGNORE_ABSTRACT_DEPRECATED_OVERRIDES = false + IGNORE_IMPORT_STATEMENTS = false + IGNORE_METHODS_OF_DEPRECATED = false + } + + override fun getDisplayName(): String { + return P3cBundle.getMessage("$messageKey.message") + } + + override fun ruleName(): String { + return "AvoidUseDeprecationApiRule" + } + + override fun getShortName(): String { + return "AliDeprecation" + } + + override fun getStaticDescription(): String? { + return P3cBundle.getMessage("$messageKey.desc") + } + + override fun createOptionsPanel(): JComponent? { + return null + } + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + val p3cConfig = P3cConfig::class.java.getService() + return when (p3cConfig.locale) { + P3cConfig.localeEn -> super.buildVisitor(holder, isOnTheFly) + else -> super.buildVisitor(DeprecationInspectionProblemsHolder(holder, isOnTheFly), isOnTheFly) + } + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return HighlightDisplayLevels.CRITICAL + } + + class DeprecationInspectionProblemsHolder(private val holder: ProblemsHolder, onTheFly: Boolean) : ProblemsHolder( + holder.manager, holder.file, onTheFly) { + + override fun registerProblem(psiElement: PsiElement, + @Nls(capitalization = Nls.Capitalization.Sentence) descriptionTemplate: String, + fixes: Array?) { + holder.registerProblem(psiElement, getMessage(descriptionTemplate), *(fixes ?: emptyArray())) + } + + override fun registerProblem(psiElement: PsiElement, + @Nls(capitalization = Nls.Capitalization.Sentence) descriptionTemplate: String, + highlightType: ProblemHighlightType, fixes: Array?) { + holder.registerProblem(psiElement, getMessage(descriptionTemplate), highlightType, *(fixes ?: emptyArray())) + } + + override fun registerProblem(reference: PsiReference, descriptionTemplate: String, + highlightType: ProblemHighlightType) { + holder.registerProblem(reference, getMessage(descriptionTemplate), highlightType) + } + + override fun registerProblemForReference(reference: PsiReference, + highlightType: ProblemHighlightType, descriptionTemplate: String, + fixes: Array?) { + holder.registerProblemForReference(reference, highlightType, getMessage(descriptionTemplate), + *(fixes ?: emptyArray())) + } + + override fun registerProblem(psiElement: PsiElement, rangeInElement: TextRange?, + message: String, fixes: Array?) { + holder.registerProblem(psiElement, rangeInElement, getMessage(message), *(fixes ?: emptyArray())) + } + + override fun registerProblem(psiElement: PsiElement, message: String, + highlightType: ProblemHighlightType, rangeInElement: TextRange?, + fixes: Array?) { + holder.registerProblem(psiElement, getMessage(message), highlightType, rangeInElement, + *(fixes ?: emptyArray())) + } + + private fun getMessage(msg: String): String { + return msg.replace("is deprecated", "已经过时了").replace("Default constructor in", "默认构造函数") + .replace("Overrides deprecated method in", "重写了过时的方法") + " #loc" + } + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliMissingOverrideAnnotationInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliMissingOverrideAnnotationInspection.kt new file mode 100644 index 0000000..3bec9de --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliMissingOverrideAnnotationInspection.kt @@ -0,0 +1,153 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection.standalone + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix +import com.alibaba.p3c.idea.util.HighlightDisplayLevels +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.psi.CommonClassNames +import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.util.InheritanceUtil +import com.siyeh.ig.BaseInspectionVisitor +import com.siyeh.ig.InspectionGadgetsFix +import com.siyeh.ig.inheritance.MissingOverrideAnnotationInspection +import com.siyeh.ig.psiutils.MethodUtils +import javax.swing.JComponent + +/** + * Batch QuickFix Supported + * @author caikang + * @date 2016/12/08 + */ +class AliMissingOverrideAnnotationInspection : MissingOverrideAnnotationInspection, AliBaseInspection { + private val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliMissingOverrideAnnotationInspection" + + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + init { + ignoreAnonymousClassMethods = false + ignoreObjectMethods = false + } + + override fun getDisplayName(): String = P3cBundle.getMessage("$messageKey.message") + + override fun getStaticDescription(): String? = P3cBundle.getMessage("$messageKey.desc") + + override fun ruleName(): String = "MissingOverrideAnnotationRule" + + override fun buildErrorString(vararg infos: Any): String = P3cBundle.getMessage("$messageKey.errMsg") + + override fun createOptionsPanel(): JComponent? = null + + override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { + val fix = super.buildFix(*infos) ?: return null + return DecorateInspectionGadgetsFix(fix, + P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.standalone.AliMissingOverrideAnnotationInspection")) + } + + override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? = buildFix(psiElement) + + override fun getDefaultLevel(): HighlightDisplayLevel = HighlightDisplayLevels.BLOCKER + + override fun buildVisitor(): BaseInspectionVisitor = MissingOverrideAnnotationVisitor() + + private inner class MissingOverrideAnnotationVisitor : BaseInspectionVisitor() { + + override fun visitMethod(method: PsiMethod) { + if (method.nameIdentifier == null) { + return + } + if (method.isConstructor) { + return + } + if (method.hasModifierProperty(PsiModifier.PRIVATE) || method.hasModifierProperty(PsiModifier.STATIC)) { + return + } + val methodClass = method.containingClass ?: return + if (ignoreAnonymousClassMethods && methodClass is PsiAnonymousClass) { + return + } + if (hasOverrideAnnotation(method)) { + return + } + if (!isJdk6Override(method, methodClass) && !isJdk5Override(method, methodClass)) { + return + } + if (ignoreObjectMethods && (MethodUtils.isHashCode(method) || + MethodUtils.isEquals(method) || + MethodUtils.isToString(method))) { + return + } + registerMethodError(method) + } + + private fun hasOverrideAnnotation(element: PsiModifierListOwner): Boolean { + val modifierList = element.modifierList + return modifierList?.findAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE) != null + } + + private fun isJdk6Override(method: PsiMethod, methodClass: PsiClass): Boolean { + val superMethods = method.findSuperMethods() + var hasSupers = false + for (superMethod in superMethods) { + val superClass = superMethod.containingClass + if (!InheritanceUtil.isInheritorOrSelf(methodClass, superClass, true)) { + continue + } + hasSupers = true + if (!superMethod.hasModifierProperty(PsiModifier.PROTECTED)) { + return true + } + } + // is override except if this is an interface method + // overriding a protected method in java.lang.Object + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6501053 + return hasSupers && !methodClass.isInterface + } + + private fun isJdk5Override(method: PsiMethod, methodClass: PsiClass): Boolean { + val superMethods = method.findSuperMethods() + for (superMethod in superMethods) { + val superClass = superMethod.containingClass + if (superClass == null || !InheritanceUtil.isInheritorOrSelf(methodClass, superClass, true)) { + continue + } + if (superClass.isInterface) { + continue + } + if (methodClass.isInterface && superMethod.hasModifierProperty(PsiModifier.PROTECTED)) { + // only true for J2SE java.lang.Object.clone(), but might + // be different on other/newer java platforms + continue + } + return true + } + return false + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/MapOrSetKeyShouldOverrideHashCodeEqualsInspection.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/MapOrSetKeyShouldOverrideHashCodeEqualsInspection.kt new file mode 100644 index 0000000..b0cdd5c --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/MapOrSetKeyShouldOverrideHashCodeEqualsInspection.kt @@ -0,0 +1,194 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.inspection.standalone + +import com.alibaba.p3c.idea.NumberConstants +import com.alibaba.p3c.idea.ObjectConstants +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.alibaba.p3c.idea.util.HighlightDisplayLevels +import com.beust.jcommander.internal.Sets +import com.intellij.codeHighlighting.HighlightDisplayLevel +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.psi.CommonClassNames +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiMethodCallExpression +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeParameter +import com.intellij.psi.PsiVariable +import com.siyeh.ig.BaseInspection +import com.siyeh.ig.BaseInspectionVisitor +import org.jetbrains.annotations.NonNls + +/** + * @author caikang + * @date 2017/03/01 + */ +class MapOrSetKeyShouldOverrideHashCodeEqualsInspection : BaseInspection, AliBaseInspection { + val messageKey = "com.alibaba.p3c.idea.inspection.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsInspection" + + constructor() + /** + * For Javassist + */ + constructor(any: Any?) : this() + + override fun getDisplayName(): String { + return P3cBundle.getMessage("$messageKey.message") + } + + override fun getStaticDescription(): String? { + return P3cBundle.getMessage("$messageKey.desc") + } + + override fun buildErrorString(vararg infos: Any): String { + val type = infos[0] as PsiClassType + return String.format(P3cBundle.getMessage("$messageKey.errMsg"), type.className) + } + + override fun buildVisitor(): BaseInspectionVisitor { + return MapOrSetKeyVisitor() + } + + override fun ruleName(): String { + return "MapOrSetKeyShouldOverrideHashCodeEqualsRule" + } + + override fun getDefaultLevel(): HighlightDisplayLevel { + return HighlightDisplayLevels.CRITICAL + } + + internal enum class ClassType { + /** + * parameter type is Set + */ + SET, + MAP, OTHER; + + override fun toString(): String { + val string = super.toString() + return string[0] + string.substring(1).toLowerCase() + } + } + + private class MapOrSetKeyVisitor : BaseInspectionVisitor() { + + private fun getClassType(aClass: PsiClass?): ClassType { + return isMapOrSet(aClass, Sets.newHashSet()) + } + + private fun isMapOrSet(aClass: PsiClass?, visitedClasses: MutableSet): ClassType { + if (aClass == null) { + return ClassType.OTHER + } + if (!visitedClasses.add(aClass)) { + return ClassType.OTHER + } + @NonNls + val className = aClass.qualifiedName + if (CommonClassNames.JAVA_UTIL_SET == className) { + return ClassType.SET + } + if (CommonClassNames.JAVA_UTIL_MAP == className) { + return ClassType.MAP + } + val supers = aClass.supers + return supers + .map { isMapOrSet(it, visitedClasses) } + .firstOrNull { it != ClassType.OTHER } + ?: ClassType.OTHER + } + + override fun visitVariable(variable: PsiVariable) { + super.visitVariable(variable) + val typeElement = variable.typeElement ?: return + val type = typeElement.type as? PsiClassType ?: return + val referenceElement = typeElement.innermostComponentReferenceElement ?: return + val aClass = type.resolve() + + val collectionType = getClassType(aClass) + if (collectionType == ClassType.OTHER) { + return + } + val parameterList = referenceElement.parameterList + if (parameterList == null || parameterList.typeParameterElements.size == NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { + return + } + val psiType = parameterList.typeArguments[0] + if (!redefineHashCodeEquals(psiType)) { + registerError(parameterList.typeParameterElements[0], psiType) + } + } + + override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { + val methodExpression = expression.methodExpression + val qualifierExpression = methodExpression.qualifierExpression ?: return + val type = qualifierExpression.type as? PsiClassType ?: return + val aClass = type.resolve() + + val collectionType = getClassType(aClass) + if (collectionType == ClassType.OTHER) { + return + } + @NonNls + val methodName = methodExpression.referenceName + if (collectionType == ClassType.SET && ObjectConstants.METHOD_NAME_ADD != methodName) { + return + } + if (collectionType == ClassType.MAP && ObjectConstants.METHOD_NAME_PUT != methodName) { + return + } + val argumentList = expression.argumentList + val arguments = argumentList.expressions + if (collectionType == ClassType.SET && arguments.size != NumberConstants.INTEGER_SIZE_OR_LENGTH_1) { + return + } + if (collectionType == ClassType.MAP && arguments.size != NumberConstants.INTEGER_SIZE_OR_LENGTH_2) { + return + } + val argument = arguments[0] + val argumentType = argument.type + if (argumentType == null || redefineHashCodeEquals(argumentType)) { + return + } + registerMethodCallError(expression, argumentType) + } + } + + companion object { + + private fun redefineHashCodeEquals(psiType: PsiType): Boolean { + if (psiType !is PsiClassType) { + return true + } + val psiClass = psiType.resolve() + if (psiClass == null || psiClass.containingFile == null || psiClass is PsiTypeParameter + || psiClass.isEnum || psiClass.isInterface) { + return true + } + if (psiClass.containingFile.fileType !is JavaFileType) { + return true + } + val hashCodeMethods = psiClass.findMethodsByName(ObjectConstants.METHOD_NAME_HASHCODE, false) + if (hashCodeMethods.size == NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { + return false + } + val equalsMethods = psiClass.findMethodsByName(ObjectConstants.METHOD_NAME_EQUALS, false) + return equalsMethods.size > NumberConstants.INTEGER_SIZE_OR_LENGTH_0 + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt new file mode 100644 index 0000000..8d4b751 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.pmd + +import com.google.common.base.Throwables +import com.intellij.openapi.application.ex.ApplicationUtil +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.psi.PsiFile +import net.sourceforge.pmd.PMDConfiguration +import net.sourceforge.pmd.PMDException +import net.sourceforge.pmd.Report +import net.sourceforge.pmd.Rule +import net.sourceforge.pmd.RuleContext +import net.sourceforge.pmd.RuleSet +import net.sourceforge.pmd.RuleSetFactory +import net.sourceforge.pmd.RuleSets +import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.RulesetsFactoryUtils +import java.io.IOException +import java.io.StringReader + +/** + * @author caikang + * @date 2016/12/11 + */ +class AliPmdProcessor(val rule: Rule) { + private val ruleSetFactory: RuleSetFactory + private val configuration = PMDConfiguration() + + init { + ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration) + } + + fun processFile(psiFile: PsiFile): List { + configuration.sourceEncoding = psiFile.virtualFile.charset.name() + configuration.inputPaths = psiFile.virtualFile.canonicalPath + val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile) ?: return emptyList() + val ctx = RuleContext() + val processor = SourceCodeProcessor(configuration) + val niceFileName = psiFile.virtualFile.canonicalPath!! + val report = Report.createReport(ctx, niceFileName) + val ruleSets = RuleSets() + val ruleSet = RuleSet() + ruleSet.addRule(rule) + ruleSets.addRuleSet(ruleSet) + LOG.debug("Processing " + ctx.sourceCodeFilename) + ruleSets.start(ctx) + try { + ctx.languageVersion = null + processor.processSourceCode(StringReader(document.text), ruleSets, ctx) + } catch (pmde: PMDException) { + LOG.debug("Error while processing file: " + niceFileName, pmde.cause) + report.addError(Report.ProcessingError(pmde.message, niceFileName)) + } catch (ioe: IOException) { + LOG.error("Unable to read source file: " + niceFileName, ioe) + } catch (re: RuntimeException) { + val root = Throwables.getRootCause(re) + if (root !is ApplicationUtil.CannotRunReadActionException) { + LOG.error("RuntimeException while processing file: " + niceFileName, re) + } + } + ruleSets.end(ctx) + return ctx.report.toList() + } + + companion object { + private val LOG = Logger.getInstance(AliPmdProcessor::class.java) + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt new file mode 100644 index 0000000..fb8a8cc --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt @@ -0,0 +1,159 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.alibaba.p3c.idea.pmd + +import com.alibaba.p3c.idea.config.P3cConfig +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import net.sourceforge.pmd.PMD +import net.sourceforge.pmd.PMDConfiguration +import net.sourceforge.pmd.PMDException +import net.sourceforge.pmd.RuleContext +import net.sourceforge.pmd.RuleSets +import net.sourceforge.pmd.benchmark.Benchmark +import net.sourceforge.pmd.benchmark.Benchmarker +import net.sourceforge.pmd.lang.LanguageVersion +import net.sourceforge.pmd.lang.LanguageVersionHandler +import net.sourceforge.pmd.lang.Parser +import net.sourceforge.pmd.lang.ast.Node +import net.sourceforge.pmd.lang.ast.ParseException +import net.sourceforge.pmd.lang.xpath.Initializer +import org.apache.commons.io.IOUtils +import java.io.Reader +import java.util.concurrent.TimeUnit + +class SourceCodeProcessor(private val configuration: PMDConfiguration) { + val logger = Logger.getInstance(javaClass) + + /** + * Processes the input stream against a rule set using the given input encoding. + * If the LanguageVersion is `null` on the RuleContext, it will + * be automatically determined. Any code which wishes to process files for + * different Languages, will need to be sure to either properly set the + * Language on the RuleContext, or set it to `null` first. + + * @see RuleContext.setLanguageVersion + * @see PMDConfiguration.getLanguageVersionOfFile + * @param sourceCode The Reader to analyze. + * @param ruleSets The collection of rules to process against the file. + * @param ctx The context in which PMD is operating. + * @throws PMDException if the input encoding is unsupported, the input stream could + * not be parsed, or other error is encountered. + */ + @Throws(PMDException::class) + fun processSourceCode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) { + determineLanguage(ctx) + + // make sure custom XPath functions are initialized + Initializer.initialize() + // Coarse check to see if any RuleSet applies to file, will need to do a finer RuleSet specific check later + try { + processSource(sourceCode, ruleSets, ctx) + + } catch (pe: ParseException) { + throw PMDException("Error while parsing " + ctx.sourceCodeFilename, pe) + } catch (e: Exception) { + throw PMDException("Error while processing " + ctx.sourceCodeFilename, e) + } finally { + IOUtils.closeQuietly(sourceCode) + } + } + + + private fun parse(ctx: RuleContext, sourceCode: Reader, parser: Parser): Node { + val start = System.nanoTime() + val rootNode = parser.parse(ctx.sourceCodeFilename, sourceCode) + ctx.report.suppress(parser.suppressMap) + val end = System.nanoTime() + Benchmarker.mark(Benchmark.Parser, end - start, 0) + return rootNode + } + + private fun symbolFacade(rootNode: Node, languageVersionHandler: LanguageVersionHandler) { + val start = System.nanoTime() + languageVersionHandler.getSymbolFacade(configuration.classLoader).start(rootNode) + val end = System.nanoTime() + Benchmarker.mark(Benchmark.SymbolTable, end - start, 0) + } + + private fun usesDFA(languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets) { + if (ruleSets.usesDFA(languageVersion.language)) { + val start = System.nanoTime() + val dataFlowFacade = languageVersion.languageVersionHandler.dataFlowFacade + dataFlowFacade.start(rootNode) + val end = System.nanoTime() + Benchmarker.mark(Benchmark.DFA, end - start, 0) + } + } + + private fun usesTypeResolution(languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets) { + if (ruleSets.usesTypeResolution(languageVersion.language)) { + val start = System.nanoTime() + languageVersion.languageVersionHandler.getTypeResolutionFacade(configuration.classLoader).start(rootNode) + val end = System.nanoTime() + Benchmarker.mark(Benchmark.TypeResolution, end - start, 0) + } + } + + private fun processSource(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) { + val start = System.currentTimeMillis() + val acus = listOf(getRootNode(sourceCode, ruleSets, ctx)) + logger.debug("elapsed ${System.currentTimeMillis() - start}ms to" + + " parse ast tree for file ${ctx.sourceCodeFilename}") + ruleSets.apply(acus, ctx, ctx.languageVersion.language) + } + + private fun getRootNode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext): Node { + if (!smartFoxConfig.astCacheEnable) { + return parseNode(ctx, ruleSets, sourceCode) + } + val node = nodeCache.getIfPresent(ctx.sourceCodeFilename) + if (node != null) { + return node + } + return parseNode(ctx, ruleSets, sourceCode) + } + + private fun parseNode(ctx: RuleContext, ruleSets: RuleSets, sourceCode: Reader): Node { + val languageVersion = ctx.languageVersion + val languageVersionHandler = languageVersion.languageVersionHandler + val parser = PMD.parserFor(languageVersion, configuration) + val rootNode = parse(ctx, sourceCode, parser) + symbolFacade(rootNode, languageVersionHandler) + usesDFA(languageVersion, rootNode, ruleSets) + usesTypeResolution(languageVersion, rootNode, ruleSets) + nodeCache.put(ctx.sourceCodeFilename, rootNode) + return rootNode + } + + private fun determineLanguage(ctx: RuleContext) { + // If LanguageVersion of the source file is not known, make a determination + if (ctx.languageVersion == null) { + val languageVersion = configuration.getLanguageVersionOfFile(ctx.sourceCodeFilename) + ctx.languageVersion = languageVersion + } + } + + companion object { + val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!! + private lateinit var nodeCache: Cache + + init { + reInitNodeCache(smartFoxConfig.astCacheTime) + } + + fun reInitNodeCache(expireTime: Long) { + nodeCache = CacheBuilder.newBuilder().concurrencyLevel(16) + .expireAfterWrite(expireTime, TimeUnit.MILLISECONDS) + .maximumSize(100) + .build()!! + } + + fun invalidateCache(file: String) { + nodeCache.invalidate(file) + } + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/AliPmdProcessor.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/AliPmdProcessor.kt new file mode 100644 index 0000000..c457155 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/AliPmdProcessor.kt @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.pmd.index + +import com.beust.jcommander.internal.Lists +import com.intellij.util.indexing.FileContent +import net.sourceforge.pmd.PMD +import net.sourceforge.pmd.PMDConfiguration +import net.sourceforge.pmd.RuleContext +import net.sourceforge.pmd.RuleSetFactory +import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.RulesetsFactoryUtils +import net.sourceforge.pmd.renderers.Renderer +import net.sourceforge.pmd.util.datasource.DataSource + +/** + * @author caikang + * @date 2016/12/11 + */ +class AliPmdProcessor { + private val ruleSetFactory: RuleSetFactory + private val configuration = PMDConfiguration() + + init { + configuration.ruleSets = "java-ali-pmd,vm-ali-other" + configuration.threads = 0 + ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration) + } + + fun processFile(fileContent: FileContent): List { + val renderer = InspectionRenderer() + val dataSources = Lists.newArrayList() + dataSources.add(InspectionDataSource(fileContent)) + PMD.processFiles(configuration, ruleSetFactory, dataSources, RuleContext(), + listOf(renderer)) + return renderer.getViolations() + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionDataSource.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionDataSource.kt new file mode 100644 index 0000000..10f95ae --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionDataSource.kt @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.pmd.index + +import com.intellij.util.indexing.FileContent +import net.sourceforge.pmd.util.datasource.DataSource +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream + +/** + * @author caikang + * @date 2016/12/11 + */ +class InspectionDataSource(private val fileContent: FileContent) : DataSource { + + @Throws(IOException::class) + override fun getInputStream(): InputStream { + return ByteArrayInputStream(fileContent.content) + } + + override fun getNiceFileName(shortNames: Boolean, inputFileName: String?): String { + return fileContent.fileName + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionRenderer.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionRenderer.kt new file mode 100644 index 0000000..3b96d03 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionRenderer.kt @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.pmd.index + +import net.sourceforge.pmd.Report +import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.renderers.AbstractRenderer +import net.sourceforge.pmd.renderers.Renderer +import net.sourceforge.pmd.util.datasource.DataSource +import java.io.IOException + +/** + * @author caikang + * @date 2016/12/11 + */ +class InspectionRenderer : AbstractRenderer("InspectionRenderer", "Idea Inspection for pmd result"), Renderer { + private lateinit var ruleProblems: List + + override fun defaultFileExtension(): String? { + return null + } + + @Throws(IOException::class) + override fun start() { + } + + override fun startFileAnalysis(dataSource: DataSource) { + } + + @Throws(IOException::class) + override fun renderFileReport(report: Report) { + ruleProblems = report.map { it } + } + + @Throws(IOException::class) + override fun end() { + } + + fun getViolations(): List { + return ruleProblems + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AliQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AliQuickFix.kt new file mode 100644 index 0000000..7ce8ff5 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AliQuickFix.kt @@ -0,0 +1,88 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiIdentifier +import com.intellij.psi.PsiLocalVariable +import com.intellij.psi.PsiMember + +/** + * + * + * @author caikang + * @date 2017/02/27 + */ +interface AliQuickFix : LocalQuickFix { + + val ruleName: String + val onlyOnThFly: Boolean + + override fun getFamilyName(): String { + return groupName + } + + companion object { + const val groupName = "Ali QuickFix" + + fun doQuickFix(newIdentifier: String, project: Project, psiIdentifier: PsiIdentifier) { + val offset = psiIdentifier.textOffset + if (psiIdentifier.parent !is PsiMember && psiIdentifier.parent !is PsiLocalVariable) { + return + } + + val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return + editor.caretModel.moveToOffset(psiIdentifier.textOffset) + val anAction = ActionManager.getInstance().getAction("RenameElement") + val psiFile = psiIdentifier.containingFile + commitDocumentIfNeeded(psiFile, project) + val event = AnActionEvent.createFromDataContext("MainMenu", anAction.templatePresentation) { + when (it) { + CommonDataKeys.PROJECT.name -> project + CommonDataKeys.EDITOR.name -> editor + CommonDataKeys.PSI_FILE.name -> psiFile + CommonDataKeys.PSI_ELEMENT.name -> psiIdentifier.parent + else -> null + } + } + val psiFacade = JavaPsiFacade.getInstance(project) + val factory = psiFacade.elementFactory + + anAction.actionPerformed(event) + + // origin PsiIdentifier is unavailable + psiFile.findElementAt(offset)?.replace(factory.createIdentifier(newIdentifier)) + } + + private fun commitDocumentIfNeeded(file: PsiFile?, project: Project) { + if (file == null) { + return + } + val manager = PsiDocumentManager.getInstance(project) + val cachedDocument = manager.getCachedDocument(file) ?: return + manager.commitDocument(cachedDocument) + } + + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AvoidStartWithDollarAndUnderLineNamingQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AvoidStartWithDollarAndUnderLineNamingQuickFix.kt new file mode 100644 index 0000000..24e9859 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AvoidStartWithDollarAndUnderLineNamingQuickFix.kt @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiIdentifier +import org.apache.commons.lang3.StringUtils + +/** + * + * + * @author caikang + * @date 2017/02/28 + */ +object AvoidStartWithDollarAndUnderLineNamingQuickFix : AliQuickFix { + override val ruleName: String + get() = "AvoidStartWithDollarAndUnderLineNamingRule" + override val onlyOnThFly: Boolean + get() = true + + override fun getName(): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.delete_$") + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return + val identifier = psiIdentifier.text + val resultName = StringUtils.replacePattern(identifier, "^[\$_]+", "") + if (resultName.toLongOrNull() != null) { + return + } + AliQuickFix.doQuickFix(resultName, project, psiIdentifier) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ClassMustHaveAuthorQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ClassMustHaveAuthorQuickFix.kt new file mode 100644 index 0000000..f9fb072 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ClassMustHaveAuthorQuickFix.kt @@ -0,0 +1,87 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.javadoc.PsiDocToken +import com.siyeh.ig.InspectionGadgetsFix + +/** + * + * + * @author caikang + * @date 2017/02/27 + */ +object ClassMustHaveAuthorQuickFix : InspectionGadgetsFix(), AliQuickFix { + + val tag = "@author ${System.getProperty("user.name") ?: System.getenv("USER")}" + + override fun doFix(project: Project?, descriptor: ProblemDescriptor?) { + descriptor ?: return + val psiClass = descriptor.psiElement as? PsiClass ?: descriptor.psiElement?.parent as? PsiClass ?: return + + val document = psiClass.docComment + val psiFacade = JavaPsiFacade.getInstance(project) + val factory = psiFacade.elementFactory + if (document == null) { + val doc = factory.createDocCommentFromText(""" +/** + * $tag + */ +""") + if (psiClass.isEnum) { + psiClass.containingFile.addAfter(doc, psiClass.prevSibling) + } else { + psiClass.addBefore(doc, psiClass.firstChild) + } + return + } + + val regex = Regex("Created by (.*) on (.*)\\.") + for (line in document.descriptionElements) { + if (line is PsiDocToken && line.text.contains(regex)) { + val groups = regex.find(line.text)?.groups ?: continue + val author = groups[1]?.value ?: continue + val date = groups[2]?.value ?: continue + document.addBefore(factory.createDocTagFromText("@date $date"), line) + document.addBefore(factory.createDocTagFromText("@author $author"), line) + line.delete() + return + } + } + + if (document.tags.isNotEmpty()) { + document.addBefore(factory.createDocTagFromText(tag), document.tags[0]) + return + } + + document.add(factory.createDocTagFromText(tag)) + } + + override val ruleName: String + get() = "ClassMustHaveAuthorRule" + override val onlyOnThFly: Boolean + get() = true + + override fun getName(): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.generate.author") + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ConstantFieldShouldBeUpperCaseQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ConstantFieldShouldBeUpperCaseQuickFix.kt new file mode 100644 index 0000000..ba7c485 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ConstantFieldShouldBeUpperCaseQuickFix.kt @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiIdentifier + +/** + * + * + * @author caikang + * @date 2017/02/28 + */ +object ConstantFieldShouldBeUpperCaseQuickFix : AliQuickFix { + override fun getName(): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.field.to.upperCaseWithUnderscore") + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return + val identifier = psiIdentifier.text + val resultName = separateCamelCase(identifier, "_").toUpperCase() + AliQuickFix.doQuickFix(resultName, project, psiIdentifier) + } + + internal fun separateCamelCase(name: String, separator: String): String { + val translation = StringBuilder() + for (i in 0..name.length - 1) { + val character = name[i] + if (Character.isUpperCase(character) && translation.isNotEmpty()) { + translation.append(separator) + } + translation.append(character) + } + return translation.toString() + } + + override val ruleName: String + get() = "ConstantFieldShouldBeUpperCaseRule" + override val onlyOnThFly: Boolean + get() = true + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/DecorateInspectionFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/DecorateInspectionFix.kt new file mode 100644 index 0000000..052bf56 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/DecorateInspectionFix.kt @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.siyeh.ig.InspectionGadgetsFix + +/** + * + * + * @author caikang + * @date 2017/03/02 + */ +class DecorateInspectionGadgetsFix(val fix: InspectionGadgetsFix, + internal val name: String, + internal val familyName: String = name) : InspectionGadgetsFix() { + override fun getName(): String { + return name + } + + override fun doFix(project: Project, descriptor: ProblemDescriptor) { + fix.applyFix(project, descriptor) + } + + + override fun getFamilyName(): String { + return familyName + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/LowerCamelCaseVariableNamingQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/LowerCamelCaseVariableNamingQuickFix.kt new file mode 100644 index 0000000..adb9315 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/LowerCamelCaseVariableNamingQuickFix.kt @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.alibaba.p3c.idea.i18n.P3cBundle +import com.google.common.base.Joiner +import com.google.common.base.Splitter +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiIdentifier + +/** + * + * + * @author caikang + * @date 2017/02/28 + */ +object LowerCamelCaseVariableNamingQuickFix : AliQuickFix { + override val ruleName: String + get() = "LowerCamelCaseVariableNamingRule" + override val onlyOnThFly: Boolean + get() = true + + override fun getName(): String { + return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.variable.lowerCamelCase") + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return + val identifier = psiIdentifier.text + val resultName = toLowerCamelCase(identifier) + AliQuickFix.doQuickFix(resultName, project, psiIdentifier) + } + + private fun toLowerCamelCase(identifier: String): String { + val list = Splitter.onPattern("[^a-z0-9A-Z]+").trimResults().omitEmptyStrings().splitToList(identifier) + val result = list.mapIndexed { i, s -> + if (i == 0) { + s.toLowerCase() + } else { + val charArray = s.toLowerCase().toCharArray() + charArray[0] = charArray[0].toUpperCase() + String(charArray) + } + } + return Joiner.on("").join(result) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/VmQuietReferenceQuickFix.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/VmQuietReferenceQuickFix.kt new file mode 100644 index 0000000..333b401 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/VmQuietReferenceQuickFix.kt @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.quickfix + +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project + +/** + * + * + * @author caikang + * @date 2017/01/26 + */ +object VmQuietReferenceQuickFix : AliQuickFix { + override val onlyOnThFly: Boolean + get() = true + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val textRange = descriptor.textRangeInElement ?: return + val document = FileDocumentManager.getInstance().getDocument( + descriptor.startElement.containingFile.virtualFile) ?: return + document.insertString(textRange.startOffset + 1, "!") + } + + override val ruleName = "UseQuietReferenceNotationRule" + + override fun getName(): String { + return "为变量添加!" + } + +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/DocumentUtils.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/DocumentUtils.kt new file mode 100644 index 0000000..68e8ccb --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/DocumentUtils.kt @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.intellij.openapi.editor.Document +import com.intellij.openapi.util.TextRange + +/** + * + * + * @author caikang + * @date 2017/03/16 +6 + */ +object DocumentUtils { + private val PMD_TAB_SIZE = 8 + fun calculateRealOffset(document: Document, line: Int, pmdColumn: Int): Int { + val maxLine = document.lineCount + if (maxLine < line) { + return -1 + } + val lineOffset = document.getLineStartOffset(line - 1) + return lineOffset + calculateRealColumn(document, line, pmdColumn) + } + + fun calculateRealColumn(document: Document, line: Int, pmdColumn: Int): Int { + var realColumn = pmdColumn - 1 + val minusSize = PMD_TAB_SIZE - 1 + val docLine = line - 1 + val lineStartOffset = document.getLineStartOffset(docLine) + val lineEndOffset = document.getLineEndOffset(docLine) + val text = document.getText(TextRange(lineStartOffset, lineEndOffset)) + + text.forEachIndexed { i, c -> + if (c == '\t') { + realColumn -= minusSize + } + if (i >= realColumn) { + return@forEachIndexed + } + } + + return realColumn + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightDisplayLevels.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightDisplayLevels.kt new file mode 100644 index 0000000..4c42da9 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightDisplayLevels.kt @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.intellij.codeHighlighting.HighlightDisplayLevel + +/** + * + * + * @author caikang + * @date 2017/02/04 + */ +object HighlightDisplayLevels { + val BLOCKER = HighlightDisplayLevel(HighlightSeverities.BLOCKER, HighlightDisplayLevel.ERROR.icon) + val CRITICAL = HighlightDisplayLevel(HighlightSeverities.CRITICAL, HighlightDisplayLevel.WARNING.icon) + val MAJOR = HighlightDisplayLevel(HighlightSeverities.MAJOR, HighlightDisplayLevel.WEAK_WARNING.icon) +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightInfoTypes.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightInfoTypes.kt new file mode 100644 index 0000000..16504ca --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightInfoTypes.kt @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.intellij.codeInsight.daemon.impl.HighlightInfoType +import com.intellij.openapi.editor.colors.CodeInsightColors + +/** + * + * + * @author caikang + * @date 2017/02/04 + */ +object HighlightInfoTypes { + val BLOCKER: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.BLOCKER, + CodeInsightColors.ERRORS_ATTRIBUTES) + val CRITICAL: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.CRITICAL, + CodeInsightColors.WARNINGS_ATTRIBUTES) + val MAJOR: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.MAJOR, + CodeInsightColors.WEAK_WARNING_ATTRIBUTES) +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightSeverities.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightSeverities.kt new file mode 100644 index 0000000..5f75753 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightSeverities.kt @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.intellij.lang.annotation.HighlightSeverity + +/** + * + * + * @author caikang + * @date 2017/02/04 + */ +object HighlightSeverities { + val MAJOR = HighlightSeverity("MAJOR", 397) + + + /** + * The standard severity level for warning annotations. + */ + val CRITICAL = HighlightSeverity("CRITICAL", 398) + + /** + * The standard severity level for error annotations. + */ + val BLOCKER = HighlightSeverity("BLOCKER", 399) +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/ProblemsUtils.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/ProblemsUtils.kt new file mode 100644 index 0000000..bdf59da --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/ProblemsUtils.kt @@ -0,0 +1,134 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiField +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiIdentifier +import com.intellij.psi.PsiJavaToken +import com.intellij.psi.PsiKeyword +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.ElementType + +/** + * + * + * @author caikang + * @date 2017/03/16 +6 + */ +object ProblemsUtils { + private val highlightLineRules = setOf("AvoidCommentBehindStatement") + fun createProblemDescriptorForPmdRule(psiFile: PsiFile, manager: InspectionManager, isOnTheFly: Boolean, + ruleName: String, desc: String, start: Int, end: Int, + checkLine: Int = 0, + quickFix: (PsiElement) -> LocalQuickFix? = { + QuickFixes.getQuickFix(ruleName, isOnTheFly) + }): ProblemDescriptor? { + val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile) ?: return null + if (highlightLineRules.contains(ruleName) && checkLine <= document.lineCount) { + val lineNumber = if (start >= document.textLength) { + document.lineCount - 1 + } else { + document.getLineNumber(start) + } + val textRange = TextRange(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber)) + return createTextRangeProblem(manager, textRange, isOnTheFly, psiFile, ruleName, desc) + } + if (psiFile.virtualFile.canonicalPath!!.endsWith(".vm")) { + return createTextRangeProblem(manager, TextRange(start, end), isOnTheFly, psiFile, ruleName, desc) + } + var psiElement = psiFile.findElementAt(start) ?: return null + + psiElement = transform(psiElement) ?: return null + var endElement = if (start == end) psiElement else getEndElement(psiFile, psiElement, end) + if (psiElement != endElement && endElement.parent is PsiField) { + psiElement = endElement + } + if (endElement is PsiWhiteSpace) { + endElement = psiElement + } + if (psiElement is PsiWhiteSpace) { + val textRange = TextRange(start, end) + return createTextRangeProblem(manager, textRange, isOnTheFly, psiFile, ruleName, desc) + } + + if (psiElement.textRange.startOffset >= endElement.textRange.endOffset) { + if (!(psiElement is PsiFile && endElement is PsiFile)) { + return null + } + endElement = psiElement + } + return manager.createProblemDescriptor(psiElement, endElement, + desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, + quickFix(psiElement)) + } + + private fun getEndElement(psiFile: PsiFile, psiElement: PsiElement, endOffset: Int): PsiElement { + var endElement = psiFile.findElementAt(endOffset) + if (endElement is PsiJavaToken && endElement.tokenType === ElementType.SEMICOLON) { + endElement = psiFile.findElementAt(endOffset - 1) + } + if (endElement is PsiIdentifier) { + return endElement + } + if (psiElement is PsiIdentifier) { + return psiElement + } + if (endElement == null || endElement is PsiWhiteSpace + || psiElement.textRange.startOffset >= endElement.textRange.endOffset) { + endElement = psiElement + } + return endElement + } + + private fun transform(element: PsiElement): PsiElement? { + var psiElement: PsiElement? = element + while (psiElement is PsiWhiteSpace) { + psiElement = psiElement.getNextSibling() + } + if (psiElement == null) { + return null + } + if (psiElement is PsiKeyword && psiElement.text != null && (com.alibaba.p3c.idea.ObjectConstants.CLASS_LITERAL == psiElement.text + || com.alibaba.p3c.idea.ObjectConstants.INTERFACE_LITERAL == psiElement.text + || com.alibaba.p3c.idea.ObjectConstants.ENUM_LITERAL == psiElement.text) && psiElement.parent is PsiClass) { + val parent = psiElement.parent as PsiClass + val identifier = parent.nameIdentifier + return identifier ?: psiElement + } + return psiElement + } + + private fun createTextRangeProblem(manager: InspectionManager, textRange: TextRange, isOnTheFly: Boolean, + psiFile: PsiFile, ruleName: String, desc: String, + quickFix: () -> LocalQuickFix? = { + QuickFixes.getQuickFix(ruleName, isOnTheFly) + }): ProblemDescriptor { + + return manager.createProblemDescriptor(psiFile, textRange, + desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + isOnTheFly, quickFix()) + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/QuickFixes.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/QuickFixes.kt new file mode 100644 index 0000000..b6aae97 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/QuickFixes.kt @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.util + +import com.alibaba.p3c.idea.quickfix.AvoidStartWithDollarAndUnderLineNamingQuickFix +import com.alibaba.p3c.idea.quickfix.ClassMustHaveAuthorQuickFix +import com.alibaba.p3c.idea.quickfix.ConstantFieldShouldBeUpperCaseQuickFix +import com.alibaba.p3c.idea.quickfix.LowerCamelCaseVariableNamingQuickFix +import com.alibaba.p3c.idea.quickfix.VmQuietReferenceQuickFix +import com.intellij.codeInspection.LocalQuickFix + +/** + * + * + * @author caikang + * @date 2017/02/06 + */ +object QuickFixes { + val quickFixes = mutableMapOf(VmQuietReferenceQuickFix.ruleName to VmQuietReferenceQuickFix, + ClassMustHaveAuthorQuickFix.ruleName to ClassMustHaveAuthorQuickFix, + ConstantFieldShouldBeUpperCaseQuickFix.ruleName to ConstantFieldShouldBeUpperCaseQuickFix, + AvoidStartWithDollarAndUnderLineNamingQuickFix.ruleName to AvoidStartWithDollarAndUnderLineNamingQuickFix, + LowerCamelCaseVariableNamingQuickFix.ruleName to LowerCamelCaseVariableNamingQuickFix) + + fun getQuickFix(rule: String, isOnTheFly: Boolean): LocalQuickFix? { + val quickFix = quickFixes[rule] ?: return null + if (!quickFix.onlyOnThFly) { + return quickFix + } + if (!isOnTheFly && quickFix.onlyOnThFly) { + return null + } + return quickFix + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt new file mode 100644 index 0000000..2bfda7a --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt @@ -0,0 +1,198 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.vcs + +import com.alibaba.p3c.idea.action.AliInspectionAction +import com.alibaba.p3c.idea.compatible.inspection.Inspections +import com.alibaba.p3c.idea.config.P3cConfig +import com.alibaba.p3c.idea.inspection.AliBaseInspection +import com.alibaba.smartfox.idea.common.util.BalloonNotifications +import com.intellij.analysis.AnalysisScope +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.ex.InspectionManagerEx +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Computable +import com.intellij.openapi.util.Ref +import com.intellij.openapi.vcs.CheckinProjectPanel +import com.intellij.openapi.vcs.VcsBundle +import com.intellij.openapi.vcs.changes.CommitExecutor +import com.intellij.openapi.vcs.checkin.CheckinHandler +import com.intellij.openapi.vcs.checkin.CheckinHandlerUtil +import com.intellij.openapi.vcs.ui.RefreshableOnComponent +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiManager +import com.intellij.ui.NonFocusableCheckBox +import com.intellij.util.PairConsumer +import com.intellij.vcsUtil.Rethrow +import java.awt.BorderLayout +import java.util.ArrayList +import java.util.Arrays +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import javax.swing.JComponent +import javax.swing.JPanel + +/** + * + * @author yaohui.wyh + * @date 2017/03/21 + * @author caikang + * @date 2017/05/04 + */ +class AliCodeAnalysisCheckinHandler( + private val myProject: Project, + private val myCheckinPanel: CheckinProjectPanel +) : CheckinHandler() { + private val dialogTitle = "Alibaba Code Analyze" + private val cancelText = "&Cancel" + private val commitText = "&Commit Anyway" + private val waitingText = "Wait" + + val log = Logger.getInstance(javaClass) + + override fun getBeforeCheckinConfigurationPanel(): RefreshableOnComponent? { + val checkBox = NonFocusableCheckBox("Alibaba Code Guidelines") + return object : RefreshableOnComponent { + override fun getComponent(): JComponent { + val panel = JPanel(BorderLayout()) + panel.add(checkBox) + val dumb = DumbService.isDumb(myProject) + checkBox.isEnabled = !dumb + checkBox.toolTipText = if (dumb) { + "Code analysis is impossible until indices are up-to-date" + } else { + "" + } + return panel + } + + override fun refresh() {} + + override fun saveState() { + getSettings().analysisBeforeCheckin = checkBox.isSelected + } + + override fun restoreState() { + checkBox.isSelected = getSettings().analysisBeforeCheckin + } + } + } + + private fun getSettings(): P3cConfig { + return ServiceManager.getService(P3cConfig::class.java) + } + + + override fun beforeCheckin(executor: CommitExecutor?, + additionalDataConsumer: PairConsumer): CheckinHandler.ReturnResult { + if (!getSettings().analysisBeforeCheckin) { + return CheckinHandler.ReturnResult.COMMIT + } + if (DumbService.getInstance(myProject).isDumb) { + if (Messages.showOkCancelDialog(myProject, + "Code analysis is impossible until indices are up-to-date", dialogTitle, + waitingText, commitText, null) == Messages.OK) { + return CheckinHandler.ReturnResult.CANCEL + } + return CheckinHandler.ReturnResult.COMMIT + } + + val virtualFiles = CheckinHandlerUtil.filterOutGeneratedAndExcludedFiles(myCheckinPanel.virtualFiles, myProject) + val hasViolation = hasViolation(virtualFiles, myProject) + if (!hasViolation) { + BalloonNotifications.showSuccessNotification("No suspicious code found!", + myProject, "Analyze Finished") + return CheckinHandler.ReturnResult.COMMIT + } + if (Messages.showOkCancelDialog(myProject, "Found suspicious code,continue commit?", + dialogTitle, commitText, cancelText, null) == Messages.OK) { + return CheckinHandler.ReturnResult.COMMIT + } else { + doAnalysis(myProject, virtualFiles.toTypedArray()) + return CheckinHandler.ReturnResult.CLOSE_WINDOW + } + } + + fun doAnalysis(project: Project, virtualFiles: Array) { + val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx + val analysisScope = AnalysisScope(project, + ArrayList(Arrays.asList(*virtualFiles))) + val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } + AliInspectionAction.createContext(tools, managerEx, null, false) + .doInspections(analysisScope) + } + + private fun hasViolation(virtualFiles: List, project: Project): Boolean { + ApplicationManager.getApplication().assertIsDispatchThread() + PsiDocumentManager.getInstance(myProject).commitAllDocuments() + if (ApplicationManager.getApplication().isWriteAccessAllowed) throw RuntimeException( + "Must not run under write action") + val result = AtomicBoolean(false) + val exception = Ref.create() + ProgressManager.getInstance().run( + object : Task.Modal(myProject, VcsBundle.message("checking.code.smells.progress.title"), true) { + override fun run(progress: ProgressIndicator) { + try { + val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } + val inspectionManager = InspectionManager.getInstance(project) + val psiManager = PsiManager.getInstance(project) + val count = AtomicInteger(0) + val hasViolation = virtualFiles.asSequence().any { + file -> + ApplicationManager.getApplication().runReadAction(Computable { + val psiFile = psiManager.findFile(file) ?: return@Computable false + val curCount = count.incrementAndGet() + progress.text = file.canonicalPath + progress.fraction = curCount.toDouble() / virtualFiles.size.toDouble() + return@Computable tools.any { + progress.checkCanceled() + val tool = it.tool as LocalInspectionTool + val aliTool = tool as AliBaseInspection + progress.text2 = aliTool.ruleName() + val problems = tool.processFile(psiFile, inspectionManager) + problems.size > 0 + } + }) + + } + result.set(hasViolation) + } catch (e: ProcessCanceledException) { + result.set(false) + } catch (e: Exception) { + log.error(e) + exception.set(e) + } + } + }) + if (!exception.isNull) { + val t = exception.get() + Rethrow.reThrowRuntime(t) + } + + return result.get() + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandlerFactory.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandlerFactory.kt new file mode 100644 index 0000000..909aad0 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandlerFactory.kt @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.idea.vcs + +import com.intellij.openapi.vcs.CheckinProjectPanel +import com.intellij.openapi.vcs.changes.CommitContext +import com.intellij.openapi.vcs.checkin.CheckinHandler +import com.intellij.openapi.vcs.checkin.CheckinHandlerFactory + +/** + * + * + * @author caikang + * @date 2017/05/04 + */ +class AliCodeAnalysisCheckinHandlerFactory : CheckinHandlerFactory() { + override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler { + return AliCodeAnalysisCheckinHandler(panel.project, panel) + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseApplicationComponent.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseApplicationComponent.kt new file mode 100644 index 0000000..6531f68 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseApplicationComponent.kt @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.idea.common.component + +import com.alibaba.smartfox.idea.common.util.PluginVersions +import com.intellij.openapi.components.ApplicationComponent + +/** + * + * + * @author caikang + * @date 2017/05/11 + */ +interface AliBaseApplicationComponent : ApplicationComponent { + override fun getComponentName(): String { + return "${PluginVersions.pluginId.idString}-${javaClass.name}" + } + + override fun disposeComponent() { + } + + override fun initComponent() { + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseProjectComponent.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseProjectComponent.kt new file mode 100644 index 0000000..39c0078 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseProjectComponent.kt @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.idea.common.component + +import com.alibaba.smartfox.idea.common.util.PluginVersions +import com.intellij.openapi.components.ProjectComponent + +/** + * + * + * @author caikang + * @date 2017/04/28 + */ +interface AliBaseProjectComponent : ProjectComponent { + override fun getComponentName(): String { + return "${PluginVersions.pluginId.idString}-${javaClass.name}" + } + + override fun disposeComponent() { + } + + override fun projectClosed() { + } + + override fun initComponent() { + } + + override fun projectOpened() { + } +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/BalloonNotifications.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/BalloonNotifications.kt new file mode 100644 index 0000000..6963bbb --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/BalloonNotifications.kt @@ -0,0 +1,111 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.idea.common.util + +import com.intellij.notification.NotificationDisplayType +import com.intellij.notification.NotificationGroup +import com.intellij.notification.NotificationListener +import com.intellij.notification.NotificationType +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectManager +import com.intellij.openapi.ui.Messages +import java.awt.Component +import java.net.UnknownHostException + +/** + * + * + * @author caikang + * @date 2017/05/08 + */ +object BalloonNotifications { + val displayId = "SmartFox Intellij IDEA Balloon Notification" + val balloonGroup = NotificationGroup(displayId, NotificationDisplayType.BALLOON, true) + + val stickyBalloonDisplayId = "SmartFox Intellij IDEA Notification" + val stickyBalloonGroup = NotificationGroup(stickyBalloonDisplayId, NotificationDisplayType.STICKY_BALLOON, true) + val TITLE = "SmartFox Intellij IDEA Plugin" + + fun showInfoDialog(component: Component, title: String, message: String) { + Messages.showInfoMessage(component, message, title) + } + + fun showErrorDialog(component: Component, title: String, errorMessage: String) { + Messages.showErrorDialog(component, errorMessage, title) + } + + fun showErrorDialog(component: Component, title: String, e: Exception) { + if (isOperationCanceled(e)) { + return + } + Messages.showErrorDialog(component, getErrorTextFromException(e), title) + } + + fun showSuccessNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, + title: String = TITLE, sticky: Boolean = false) { + showNotification(message, project, title, NotificationType.INFORMATION, null, sticky) + } + + fun showWarnNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, + title: String = TITLE, sticky: Boolean = false) { + showNotification(message, project, title, NotificationType.WARNING, null, sticky) + } + + fun showErrorNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, + title: String = TITLE, sticky: Boolean = false) { + showNotification(message, project, title, NotificationType.ERROR, null, sticky) + } + + fun showSuccessNotification(message: String, project: Project?, + notificationListener: NotificationListener, title: String = TITLE, sticky: Boolean = false) { + showNotification(message, project, title, NotificationType.INFORMATION, notificationListener, sticky) + } + + fun showNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, + title: String = TITLE, + notificationType: NotificationType = NotificationType.INFORMATION, + notificationListener: NotificationListener? = null, sticky: Boolean = false) { + val group = if (sticky) { + stickyBalloonGroup + } else { + balloonGroup + } + group.createNotification(title, message, notificationType, notificationListener).notify(project) + } + + private fun isOperationCanceled(e: Exception): Boolean { + return e is ProcessCanceledException + } + + fun getErrorTextFromException(e: Exception): String { + if (e is UnknownHostException) { + return "Unknown host: " + e.message + } + return e.message ?: "" + } +} + +object LogNotifications { + val group = NotificationGroup(BalloonNotifications.displayId, NotificationDisplayType.NONE, true) + + fun log(message: String, project: Project? = ProjectManager.getInstance().defaultProject, + title: String = BalloonNotifications.TITLE, + notificationType: NotificationType = NotificationType.INFORMATION, + notificationListener: NotificationListener? = null) { + group.createNotification(title, message, notificationType, notificationListener).notify(project) + } +} diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/CommonExtensions.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/CommonExtensions.kt new file mode 100644 index 0000000..7b77198 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/CommonExtensions.kt @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.idea.common.util + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project + +/** + * + * + * @author caikang + * @date 2017/06/19 + */ +fun Class.getService(): T { + return ServiceManager.getService(this) +} + +fun Class.getService(project: Project): T { + return ServiceManager.getService(project, this) +} \ No newline at end of file diff --git a/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/PluginVersions.kt b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/PluginVersions.kt new file mode 100644 index 0000000..993c040 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/PluginVersions.kt @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.smartfox.idea.common.util + +import com.intellij.ide.plugins.IdeaPluginDescriptor +import com.intellij.ide.plugins.PluginManager +import com.intellij.ide.plugins.cl.PluginClassLoader +import com.intellij.openapi.application.ApplicationInfo +import com.intellij.openapi.extensions.PluginId + +/** + * @author caikang + */ +object PluginVersions { + val baseVersion141 = 141 + val baseVersion143 = 143 + val baseVersion145 = 145 + val baseVersion162 = 162 + val baseVersion163 = 163 + val baseVersion171 = 171 + + val pluginId: PluginId = (javaClass.classLoader as PluginClassLoader).pluginId + val pluginDescriptor: IdeaPluginDescriptor = PluginManager.getPlugin(pluginId)!! + + /** + * 获取当前安装的 plugin版本 + */ + val pluginVersion: String + get() = pluginDescriptor.version + + /** + * 获取当前使用的IDE版本 + */ + val ideVersion: String + get() { + val applicationInfo = ApplicationInfo.getInstance() + return applicationInfo.fullVersion + "_" + applicationInfo.build + } + + + val baseVersion: Int + get() { + val applicationInfo = ApplicationInfo.getInstance() + return applicationInfo.build.baselineVersion + } +} diff --git a/idea-plugin/p3c-common/src/main/resources/icons/ali-ide-run.png b/idea-plugin/p3c-common/src/main/resources/icons/ali-ide-run.png new file mode 100644 index 0000000..754649e Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/ali-ide-run.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/icons/alibaba.png b/idea-plugin/p3c-common/src/main/resources/icons/alibaba.png new file mode 100644 index 0000000..546845c Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/alibaba.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/icons/language.png b/idea-plugin/p3c-common/src/main/resources/icons/language.png new file mode 100644 index 0000000..b267454 Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/language.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/icons/qiyong.png b/idea-plugin/p3c-common/src/main/resources/icons/qiyong.png new file mode 100644 index 0000000..d8c20f1 Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/qiyong.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/icons/tingyong.png b/idea-plugin/p3c-common/src/main/resources/icons/tingyong.png new file mode 100644 index 0000000..040596f Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/tingyong.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/icons/trample.png b/idea-plugin/p3c-common/src/main/resources/icons/trample.png new file mode 100644 index 0000000..7bf78b9 Binary files /dev/null and b/idea-plugin/p3c-common/src/main/resources/icons/trample.png differ diff --git a/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle.xml b/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle.xml new file mode 100644 index 0000000..438ba94 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle.xml @@ -0,0 +1,96 @@ + + + + 阿里编码规约 + 切换语言至英文(English) + 切换语言至中文 + + 重启后生效]]> + 编码规约扫描 + 关闭实时检测功能 + 打开实时检测功能 + 删除开头的 $ 或者 _ + 添加/提取 @author + 修正为以下划线分隔大写模式 + 修改为小写驼峰命名(lowerCamelCase) + 为变量添加! + 替换为 + 通过类 '%s' 直接访问静态成员 + '%s.%s' + + 添加 @Override 注解 + 为语句加上大括号 + 翻转 equals 调用 + 修改为 String[] str 模式 + 'l' 替换为 'L' + + + #ref #loc]]> + + 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。 + + 不应该通过类实例访问静态成员 + %s #loc + + + 不能使用过时的类或方法。 + + 所有的覆写方法,必须加@Override注解。 + + + #ref()缺少 '@Override' 注解 #loc]]> + + Map/Set的key为自定义对象时,必须重写hashCode和equals。 + + + 参数类型 %s 没有重写hashCode和equals #loc + + + #ref 是非线程安全的,请加锁或者使用局部变量 #loc]]> + + #ref 没有加大括号 #loc]]> + + #ref 应该作为方法 "%s()"的调用方,而不是参数 #loc]]> + + #ref 数组定义格式错误 #loc]]> + + #ref 应该以大写L结尾 #loc]]> + + #ref #loc]]> + + + +避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。 + + +]]> + + +不能使用过时的类或方法。
+说明:java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。 + + +]]>
+ + +所有的覆写方法,必须加@Override注解。
+反例:getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。 + + +]]>
+ + + +Map/Set的key为自定义对象时,必须重写hashCode和equals。
+关于hashCode和equals的处理,遵循如下规则:
+ 1) 只要重写equals,就必须重写hashCode。
+ 2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
+ 3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
+ + +]]>
+
diff --git a/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle_en.xml b/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle_en.xml new file mode 100644 index 0000000..c6d7936 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/resources/messages/P3cBundle_en.xml @@ -0,0 +1,106 @@ + + + + Alibaba Coding Guidelines + Switch language to English + Switch language to Chinese + + restart to get effect.]]> + Alibaba Coding Guidelines Analyze + Close inspection on the fly + + Open inspection on the fly + Delete starting '$' or '_' + Add/Extract @author + Refactor to upper case with underscore + + Refactor to lower camel case name + Add '!' for variable + Replace with + Access static '%s.%s' via + class '%s' reference. + + Add @Override + annotation + + Add braces to statement + Flip 'equals()' + Replace with Java-style array + declaration + + Replace 'l' with 'L' + + + #ref should replace with equals for wrapper type #loc]]> + A static field + or method should be directly referred by its class name instead of its corresponding object name. + + %s access static + member via class instance is prohibited #loc + + + Using a deprecated class or + method is prohibited. + + An overridden + method from an interface or abstract class must be marked with @Override annotation. + + + #ref() #loc]]> + + Custom class must override 'hashCode' and 'equals' while use as key for Map or value for Set. + + + Type of parameter %s does not override 'hashCode' and 'equals' #loc + + + #ref is unsafe,lock or use local variable #loc]]> + + #ref without braces #loc]]> + + #ref is argument of '%s()', instead of its target #loc]]> + + #ref ends with lowercase 'l' #loc]]> + + + + + #ref #loc]]> + + + +A static field or method should be directly referred by its class name instead of its corresponding object name. + + +]]> + + +Using a deprecated class or method is prohibited.
+Note: For example, decode(String source, String encode) should be used instead of the deprecated method decode(String encodeStr). Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to check out what its new implementation is. + + +]]>
+ + +An overridden method from an interface or abstract class must be marked with @Override annotation.
+Counter example: For getObject() and get0bject(), the first one has a letter 'O', and the second one has a number '0'. To accurately determine whether the overriding is successful, an @Override annotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately. + + +]]>
+ + + +Custom class must override 'hashCode' and 'equals' while use as key for Map or value for Set.
+The usage of hashCode and equals should follow:
+ 1) Override hashCode if equals is overridden.
+ 2) These two methods must be overridden for Set since they are used to ensure that no duplicate object will be inserted in Set.
+ 3) These two methods must be overridden if self-defined object is used as the key of Map.
+Note: String can be used as the key of Map since these two methods have been rewritten. + + +]]>
+
diff --git a/idea-plugin/p3c-common/src/main/resources/rulesets/java/ali-pmd.xml b/idea-plugin/p3c-common/src/main/resources/rulesets/java/ali-pmd.xml new file mode 100644 index 0000000..23f86db --- /dev/null +++ b/idea-plugin/p3c-common/src/main/resources/rulesets/java/ali-pmd.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea-plugin/p3c-common/src/main/resources/tpl/StaticDescriptionTemplate.ftl b/idea-plugin/p3c-common/src/main/resources/tpl/StaticDescriptionTemplate.ftl new file mode 100644 index 0000000..41203b2 --- /dev/null +++ b/idea-plugin/p3c-common/src/main/resources/tpl/StaticDescriptionTemplate.ftl @@ -0,0 +1,12 @@ +<#ftl output_format="HTML"> + + + +
${message}<#if description??>
${description}
+
+<#list examples as example> +
${example}
+ + + + diff --git a/idea-plugin/p3c-idea/build.gradle b/idea-plugin/p3c-idea/build.gradle new file mode 100644 index 0000000..158a029 --- /dev/null +++ b/idea-plugin/p3c-idea/build.gradle @@ -0,0 +1,22 @@ +plugins { + id "org.jetbrains.intellij" version '0.2.17' +} +apply plugin: 'kotlin' +apply plugin: 'idea' + +intellij { + version idea_version + plugins 'git4idea' + pluginName plugin_name + updateSinceUntilBuild false + sandboxDirectory "idea-sandbox" +} + +version plugin_version + +dependencies { + compile group: 'org.freemarker', name: 'freemarker', version: '2.3.25-incubating' + compile 'com.alibaba.p3c.idea:p3c-common:1.0.0' + compile group: 'org.javassist', name: 'javassist', version: '3.21.0-GA' +} + diff --git a/idea-plugin/p3c-idea/src/main/resources/META-INF/p3c.xml b/idea-plugin/p3c-idea/src/main/resources/META-INF/p3c.xml new file mode 100755 index 0000000..b979516 --- /dev/null +++ b/idea-plugin/p3c-idea/src/main/resources/META-INF/p3c.xml @@ -0,0 +1,53 @@ + + + + com.alibaba.p3c.idea.component.CommonSettingsApplicationComponent + + + + + + com.alibaba.p3c.idea.component.AliProjectComponent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea-plugin/p3c-idea/src/main/resources/META-INF/plugin.xml b/idea-plugin/p3c-idea/src/main/resources/META-INF/plugin.xml new file mode 100755 index 0000000..d1e6f18 --- /dev/null +++ b/idea-plugin/p3c-idea/src/main/resources/META-INF/plugin.xml @@ -0,0 +1,18 @@ + + com.alibaba.p3c.smartfox + Alibaba Java Coding Guidelines + Alibaba Java Coding Guidelines plugin support. + + + + alibaba + 1.0.0 + + com.intellij.velocity + com.intellij.modules.java + com.intellij.modules.platform + com.intellij.modules.lang + com.intellij.modules.vcs + com.intellij.modules.xml + com.intellij.modules.xdebugger + diff --git a/idea-plugin/settings.gradle b/idea-plugin/settings.gradle new file mode 100644 index 0000000..f6db291 --- /dev/null +++ b/idea-plugin/settings.gradle @@ -0,0 +1,2 @@ +include 'p3c-idea' +include 'p3c-common' diff --git a/license.txt b/license.txt index dcbe1ee..3fed5b8 100644 --- a/license.txt +++ b/license.txt @@ -204,4 +204,4 @@ APPENDIX: How to apply the Apache License to your work distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing - permissions and limitations under the License. + permissions and limitations under the License. \ No newline at end of file diff --git a/p3c-pmd/.gitignore b/p3c-pmd/.gitignore new file mode 100644 index 0000000..ddb1f85 --- /dev/null +++ b/p3c-pmd/.gitignore @@ -0,0 +1,90 @@ +# reference to https://github.com/github/gitignore + +testdata/ +# Java gitignore # +.class +.log + +# Package Files # + +*.war +*.ear + +#hsf files +configuration + +# maven gitignore# +target/** + +.svn/ + +# intelliJ.gitignore # +*.iml +*.ipr +*.iws +*.bat + +# Eclipse git ignore# +*.pydevproject +.project +.metadata +bin/** +*/bin/** +tmp/** +tmp/**/* +configuration/** +*.tmp +*.bak +*.orig +*.swp +*~.nib +.classpath +.settings/ +.loadpath +.fileTable* +.cache + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +#log +*.log +*.log.* + +# Windows Thumbs.db +*.db + +# OSX +.DS_Store + +# sass gitignore# +.sass-cache +.idea + +# tcc_coverage +coverage.ec + + + +config.client.* + +temp/ +*.pid +*.orig + +hsf.configuration/ + +# code coverage report +*.ec + +#hsf test +*.instance diff --git a/p3c-pmd/README.md b/p3c-pmd/README.md new file mode 100644 index 0000000..0209744 --- /dev/null +++ b/p3c-pmd/README.md @@ -0,0 +1,248 @@ +# P3C-PMD + +## Build requirements +- JDK 1.7+ +- Maven 3 + +## Use as dependency + +### Maven +```xml + + com.alibaba.p3c + p3c-pmd + 1.3.0 + +``` +### Gradle +``` groovy +compile 'com.alibaba.p3c:p3c-pmd:1.3.0' +``` + +## Rules + +P3C-PMD implements 49 rules involved in *Alibaba Java Coding Guidelines*, based on PMD ([https://github.com/pmd/pmd](https://github.com/pmd/pmd)). + +### Concurrency +* 1 ``[Mandatory]`` Customized ThreadLocal variables must be recycled,especially when using thread pools in which threads are often reused. Otherwise, it may affect subsequent business logic and cause unexpected problems such as memory leak. +* 2 ``[Mandatory]`` A meaningful thread name is helpful to trace the error information, so assign a name when creating threads or thread pools. +Positive example: + + ``` + public class TimerTaskThread extends Thread { + public TimerTaskThread(){ + super.setName("TimerTaskThread"); … } + ``` +* 3 ``[Mandatory]`` Threads should be provided by thread pools. Explicitly creating threads is not allowed. +Note: Using thread pool can reduce the time of creating and destroying thread and save system resource. If we do not use thread pools, lots of similar threads will be created which lead to "running out of memory" or over-switching problems. +* 4 ``[Mandatory]`` A thread pool should be created by ThreadPoolExecutor rather than Executors. These would make the parameters of the thread pool understandable. It would also reduce the risk of running out of system resource. +Note: Below are the problems created by usage of Executors for thread pool creation: + 1. FixedThreadPool and SingleThreadPool: +  Maximum request queue size Integer.MAX_VALUE. A large number of requests might cause OOM. + 2. CachedThreadPool and ScheduledThreadPool: + The number of threads which are allowed to be created is Integer.MAX_VALUE. Creating too many threads might lead to OOM. +* 5 ``[Mandatory]`` SimpleDataFormat is unsafe, do not define it as a static variable. If have to, lock or DateUtils class must be used. +Positive example: Pay attention to thread-safety when using DateUtils. It is recommended to use as below: + + ``` + private static final ThreadLocal df = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd"); + } + }; + ``` + Note: In JDK8, Instant can be used to replace Date, Calendar is replaced by LocalDateTime, SimpleDateFormatter is replaced by DateTimeFormatter. +* 10 ``[Mandatory]`` Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions. +* 11 ``[Recommended]`` When using CountDownLatch to convert asynchronous operations to synchronous ones, each thread must call countdown method before quitting. Make sure to catch any exception during thread running, to let countdown method be executed. If main thread cannot reach await method, program will return until timeout. +Note: Be careful, exception thrown by sub-thread cannot be caught by main thread. +* 12 ``[Recommended]`` Avoid using Random instance by multiple threads. Although it is safe to share this instance, competition on the same seed will damage performance. +Note: Random instance includes instances of java.util.Random and Math.random(). +Positive example: +After JDK7, API ThreadLocalRandom can be used directly. But before JDK7, instance needs to be created in each thread. + +### Collection + +* 4 ``[Mandatory]`` Do not cast subList in class ArrayList, otherwise ClassCastException will be thrown: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. +Note: subList of ArrayList is an inner class, which is a view of ArrayList. All operations on the Sublist will affect the original list finally. +* 5 ``[Mandatory]`` When using subList, be careful to modify the size of original list. It might cause ConcurrentModificationException when performing traversing, adding or deleting on the subList. +* 6 ``[Mandatory]`` Use toArray(T[] array) to convert list to array. The input array type should be the same with the list whose size is list.size(). +Counter example: Do not use toArray method without arguments. Since the return type is Object[], ClassCastException will be thrown when casting it to a different array type. +Positive example: + + ``` + List list = new ArrayList(2); + list.add("guan"); + list.add("bao"); + String[] array = new String[list.size()]; + array = list.toArray(array); + ``` +Note: When using toArray method with arguments, if input array size is not large enough, the method will re-assign the size internally, and then return the address of new array. If the size is larger than needed, the value of index[list.size()] will be set to null while other values remain the same. Defining an input with the same size of the list is recommended. +* 7 ``[Mandatory]`` Do not use methods which will modify the list after using Arrays.asList to convert array to list, otherwise methods like add/remove/clear will throw UnsupportedOperationException. +Note: The result of asList is the inner class of Arrays, which does not implement methods to modify itself. Arrays.asList is only a transferred interface, data inside which is stored as an array. + + ``` + String[] str = new String[] { "a", "b" }; + List list = Arrays.asList(str); + ``` +Case 1: list.add("c"); will throw a runtime exception. +Case 2: str[0]= "gujin"; list.get(0) will be modified. +* 9 ``[Mandatory]`` Do not remove or add elements to a collection in a foreach loop. Please use Iterator to remove an item. Iterator object should be synchronized when executing concurrent operations. +Counter example: + + ``` + List a = new ArrayList(); + a.add("1"); + a.add("2"); + for (String temp : a) { + if ("1".equals(temp)){ + a.remove(temp); + } + } + ``` + Note: If you try to replace "1" with "2", you will get an unexpected result. +Positive example: + + ``` + Iterator it = a.iterator(); + while (it.hasNext()) { + String temp = it.next(); + if (delete condition) { + it.remove(); + } + } + ``` +* 11``[Recommended]`` Set a size when initializing a collection if possible. +Note: Better to use ArrayList(int initialCapacity) to initialize ArrayList. + + +### Naming Conventions +* 1 ``[Mandatory]`` All names should not start or end with an underline or a dollar sign. +Counter example: _name / __name / $Object / name_ / name$ / Object$ + +* 2 ``[Mandatory]`` Using Chinese, Pinyin, or Pinyin-English mixed spelling in naming is strictly prohibited. Accurate English spelling and grammar will make the code readable, understandable, and maintainable. +Positive example: alibaba / taobao / youku / Hangzhou. In these cases, Chinese proper names in Pinyin are acceptable. + +* 3 ``[Mandatory]`` Class names should be nouns in UpperCamelCase except domain models: DO, BO, DTO, VO, etc. +Positive example: MarcoPolo / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion +Counter example: macroPolo / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion + +* 4 ``[Mandatory]`` Method names, parameter names, member variable names, and local variable names should be written in lowerCamelCase. +Positive example: localValue / getHttpMessage() / inputUserId + +* 5 ``[Mandatory]`` Constant variable names should be written in upper characters separated by underscores. These names should be semantically complete and clear. +Positive example: MAX_STOCK_COUNT +Counter example: MAX_COUNT + +* 6 ``[Mandatory]`` Abstract class names must start with Abstract or Base. Exception class names must be ended with Exception. Test cases shall be started with the class names to be tested and ended with Test. + +* 7 ``[Mandatory]`` Brackets are a part of an Array type. The definition could be: String[] args; +Counter example: String args[]; + +* 8 ``[Mandatory]`` Do not add 'is' as prefix while defining Boolean variable, since it may cause a serialization exception in some Java Frameworks. +Counter example: boolean isSuccess; The method name will be isSuccess() and then RPC framework will deduce the variable name as 'success', resulting in a serialization error since it cannot find the correct attribute. +* 9 ``[Mandatory]`` Package should be named in lowercase characters. There should be only one English word after each dot. Package names are always in singular format while class name can be in plural format if necessary. +Positive example: com.alibaba.open.util can be used as package name for utils; +* 13 There are mainly two rules for interface and corresponding implementation class naming: + 1. ``[Mandatory]`` All Service and DAO classes must be interface based on SOA principle. Implementation class names should be ended with Impl. +Positive example: CacheServiceImpl to implement CacheService. + 2. ``[Recommended]`` If the interface name is to indicate the ability of the interface, then its name should be adjective. +Positive example: AbstractTranslator to implement Translatable. + +### Constant Conventions + +* 1 ``[Mandatory]`` Magic values, except for predefined, are forbidden in coding. +Counter example: String key="Id#taobao_" + tradeId; +* 2 ``[Mandatory]`` 'L' instead of 'l' should be used for long or Long variable because 'l' is easily to be regarded as number 1 in mistake. +Counter example: Long a=2l, it is hard to tell whether it is number 21 or Long 2. +### OOP +* 5 ``[Mandatory]`` Using a deprecated class or method is prohibited. +Note: For example, decode(String source, String encode) should be used instead of the deprecated method decode(String encodeStr). Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to check out what its new implementation is. +* 6 ``[Mandatory]`` Since NullPointerException can possibly be thrown while calling the equals method of Object, equals should be invoked by a constant or an object that is definitely not null. +Positive example: "test".equals(object); +Counter example: object.equals("test"); +Note: java.util.Objects#equals (a utility class in JDK7) is recommended. + +* 7 ``[Mandatory]`` The wrapper classes should be compared by equals method rather than by symbol of '==' directly. +Note: Consider this assignment: Integer var = ?. When it fits the range from -128 to 127, we can use == directly for a comparison. Because the Integer object will be generated by IntegerCache.cache, which reuses an existing object. Nevertheless, when it fits the complementary set of the former range, the Integer object will be allocated in Heap, which does not reuse an existing object. This is a pitfall. Hence the equals method is recommended. +* 8 ``[Mandatory]`` Rules for using primitive data types and wrapper classes: + 1. Members of a POJO class must be wrapper classes. + 2. The return value and arguments of a RPC method must be wrapper classes. + 3. ``[Recommended]`` Local variables should be primitive data types. +Note: In order to remind the consumer of explicit assignments, there are no initial values for members in a POJO class. As a consumer, you should check problems such as NullPointerException and warehouse entries for yourself. + Positive example: As the result of a database query may be null, assigning it to a primitive date type will cause a risk of NullPointerException because of autoboxing. + Counter example: Consider the output of a transaction volume's amplitude, like ±x%. As a primitive data, when it comes to a failure of calling a RPC service, the default return value: 0% will be assigned, which is not correct. A hyphen like - should be assigned instead. Therefore, the null value of a wrapper class can represent additional information, such as a failure of calling a RPC service, an abnormal exit, etc. +* 9 ``[Mandatory]`` While defining POJO classes like DO, DTO, VO, etc., do not assign any default values to the members. +* 12 ``[Mandatory]`` The toString method must be implemented in a POJO class. The super.toString method should be called in front of the whole implementation if the current class extends another POJO class. +Note: We can call the toString method in a POJO directly to print property values in order to check the problem when a method throws an exception in runtime. +* 17 ``[Recommended]`` Use the append method in StringBuilder inside a loop body when concatenating multiple strings. + + Counter example: + + ``` + String str = "start"; + for(int i=0; i<100; i++) { + str = str + "hello"; + } + ``` + + Note: According to the decompiled bytecode file, for each loop, it allocates a StringBuilder object, appends a string, and finally returns a String object via the toString method. This is a tremendous waste of memory. + +### Flow Control Statements +* 1 ``[Mandatory]`` In a switch block, each case should be finished by break/return. If not, a note should be included to describe at which case it will stop. Within every switch block, a default statement must be present, even if it is empty. +* 2 ``[Mandatory]`` Braces are used with if, else, for, do and while statements, even if the body contains only a single statement. Avoid using the following example: + + ``` + if (condition) statements; + ``` +* 4 ``[Recommended]`` Do not use complicated statements in conditional statements (except for frequently used methods like getXxx/isXxx). Use boolean variables to store results of complicated statements temporarily will increase the code's readability. +Note: Logic within many if statements are very complicated. Readers need to analyze the final results of the conditional expression to decide what statement is to be executed in certain conditions. +Positive example: + + ``` + // please refer to the pseudo-code as follows + boolean existed = (file.open(fileName, "w") != null) && (...) || (...); + if (existed) { + ... + } + ``` + + Counter example: + + ``` + if ((file.open(fileName, "w") != null) && (...) || (...)) { + ... + } + ``` + +### Exception +* 5 ``[Mandatory]`` Make sure to invoke the rollback if a method throws an Exception +* 7 ``[Mandatory]`` Never use return within a finally block. A return statement in a finally block will cause exceptions or result in a discarded return value in the try-catch block. +* 10 ``[Recommended]`` One of the most common errors is NullPointerException. Pay attention to the following situations: + 1. If the return type is primitive, return a value of wrapper class may cause NullPointerException. +   Counter example: public int f() { return Integer } Unboxing a null value will throw a NullPointerException. + 2. The return value of a database query might be null. + 3. Elements in collection may be null, even though Collection.isEmpty() returns false. + 4. Return values from an RPC might be null. + 5. Data stored in sessions might by null. + 6. Method chaining, like obj.getA().getB().getC(), is likely to cause NullPointerException. +   Positive example: Use Optional to avoid null check and NPE (Java 8+). + +### Code Comments +* 1 ``[Mandatory]`` Javadoc should be used for classes, class variables and methods. The format should be '/** comment **/', rather than '// xxx'. +Note: In IDE, Javadoc can be seen directly when hovering, which is a good way to improve efficiency. +* 2 ``[Mandatory]`` Abstract methods (including methods in interface) should be commented by Javadoc. Javadoc should include method instruction, description of parameters, return values and possible exceptions. +* 3 ``[Mandatory]`` Every class should include information of author(s) and date. +* 4 ``[Mandatory]`` Single line comments in a method should be put above the code to be commented, by using // and multiple lines by using /* */. Alignment for comments should be noticed carefully. +* 5 ``[Mandatory]`` All enumeration type fields should be commented as Javadoc style. + + +### Other +* ``[Mandatory]`` Avoid using *Apache Beanutils* to copy attributes. +* 1 ``[Mandatory]`` When using regex, precompile needs to be done in order to increase the matching performance. +Note: Do not define Pattern pattern = Pattern.compile(.); within method body. +* 3 ``[Mandatory]`` Variables must add exclamatory mark when passing to velocity engine from backend, like $!{var}. +Note: If attribute is null or does not exist, ${var} will be shown directly on web pages. +* 4 ``[Mandatory]`` The return type of Math.random() is double, value range is 0<=x<1 (0 is possible). If a random integer is required, do not multiply x by 10 then round the result. The correct way is to use nextInt or nextLong method which belong to Random Object. +* 5 ``[Mandatory]`` Use System.currentTimeMillis() to get the current millisecond. Do not use new Date().getTime(). +Note: In order to get a more accurate time, use System.nanoTime(). In JDK8, use Instant class to deal with situations like time statistics. \ No newline at end of file diff --git a/p3c-pmd/pom.xml b/p3c-pmd/pom.xml new file mode 100644 index 0000000..ba24173 --- /dev/null +++ b/p3c-pmd/pom.xml @@ -0,0 +1,146 @@ + + + + + org.sonatype.oss + oss-parent + 9 + + 4.0.0 + com.alibaba.p3c + p3c-pmd + 1.3.0 + jar + p3c-pmd + + UTF-8 + 5.5.2 + + + Alibaba Java Coding Guidelines PMD implementations + https://github.com/alibaba/p3c + 2017 + + + + Apache 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + + + + https://github.com/alibaba/p3c + scm:git:https://git@github.com/alibaba/p3c.git + + + + Alibaba Group + https://github.com/alibaba + + + + + XuanTan + https://github.com/xuantan + zhangym124@gmail.com + + + ChangLe + https://github.com/LQZYC + lqleo323@gmail.com + + + ZengHou + https://github.com/fw8899 + fengwei1983@gmail.com + + + ShengYan + http://smiler158.github.io/ + smiler158@163.com + + + KeQi + lyzw2009@gmail.com + + + JunLie + https://github.com/SeanCai + sean.caikang@gmail.com + + + + + + net.sourceforge.pmd + pmd-java + ${pmd.version} + + + net.sourceforge.pmd + pmd-vm + ${pmd.version} + + + net.sourceforge.pmd + pmd-test + ${pmd.version} + test + + + com.alibaba + fastjson + 1.2.9 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.7 + 1.7 + UTF-8 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + + date + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/I18nResources.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/I18nResources.java new file mode 100644 index 0000000..27da9d3 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/I18nResources.java @@ -0,0 +1,166 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.ResourceBundle.Control; + +/** + * @author caikang + * @date 2017/05/24 + */ +public class I18nResources { + private static final String XML_LITERAL = "xml"; + + private static String lang = System.getProperty("pmd.language", "zh"); + + private static Locale currentLocale; + + private static ResourceBundle resourceBundle = changeLanguage(lang); + + public static ResourceBundle changeLanguage(String language) { + Locale locale = Locale.CHINESE.getLanguage().equals(language) ? Locale.CHINESE : Locale.ENGLISH; + return changeLanguage(locale); + } + + public static ResourceBundle changeLanguage(Locale locale) { + if (currentLocale != null && currentLocale.equals(locale)) { + return resourceBundle; + } + currentLocale = locale; + resourceBundle = ResourceBundle.getBundle("messages", locale, new XmlControl()); + return resourceBundle; + } + + public static String getMessage(String key) { + return resourceBundle.getString(key).trim(); + } + + public static String getMessage(String key, Object... params) { + String value = getMessage(key); + if (params == null || params.length == 0) { + return value; + } + return String.format(value, params); + } + + public static String getMessageWithExceptionHandled(String key) { + try { + return resourceBundle.getString(key).trim(); + } catch (MissingResourceException e) { + return key; + } + } + + public static class XmlResourceBundle extends ResourceBundle { + private Properties props; + + XmlResourceBundle(InputStream stream) throws IOException { + props = new Properties(); + props.loadFromXML(stream); + } + + @Override + protected Object handleGetObject(String key) { + return props.getProperty(key); + } + + @Override + public Enumeration getKeys() { + List keys = new ArrayList<>(); + Enumeration enumeration = props.keys(); + while (enumeration.hasMoreElements()) { + keys.add((String)enumeration.nextElement()); + } + return Collections.enumeration(keys); + } + } + + public static class XmlControl extends Control { + @Override + public List getFormats(String baseName) { + if (baseName == null) { + throw new NullPointerException(); + } + return Collections.singletonList(XML_LITERAL); + } + + @Override + public Locale getFallbackLocale(String baseName, Locale locale) { + return null; + } + + @Override + public ResourceBundle newBundle(String baseName, + Locale locale, + String format, + ClassLoader loader, + boolean reload) + throws IllegalAccessException, + InstantiationException, + IOException { + if (baseName == null || locale == null + || format == null || loader == null) { + throw new NullPointerException(); + } + ResourceBundle bundle = null; + if (XML_LITERAL.equals(format)) { + String bundleName = toBundleName(baseName, locale); + String resourceName = toResourceName(bundleName, format); + InputStream stream; + if (reload) { + stream = getInputStream(loader, resourceName); + } else { + stream = loader.getResourceAsStream(resourceName); + } + if (stream != null) { + BufferedInputStream bis = new BufferedInputStream(stream); + bundle = new XmlResourceBundle(bis); + bis.close(); + } + } + return bundle; + } + + private InputStream getInputStream(ClassLoader loader, String resourceName) + throws IOException { + URL url = loader.getResource(resourceName); + if (url == null) { + return null; + } + URLConnection connection = url.openConnection(); + if (connection == null) { + return null; + } + // Disable caches to get fresh data for + // reloading. + connection.setUseCaches(false); + return connection.getInputStream(); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/fix/FixClassTypeResolver.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/fix/FixClassTypeResolver.java new file mode 100644 index 0000000..d7692e2 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/fix/FixClassTypeResolver.java @@ -0,0 +1,768 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package com.alibaba.p3c.pmd.fix; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.alibaba.p3c.pmd.lang.java.util.NumberConstants; +import com.alibaba.p3c.pmd.lang.java.util.StringAndCharConstants; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; +import net.sourceforge.pmd.lang.java.ast.ASTAndExpression; +import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; +import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; +import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression; +import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExclusiveOrExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTInclusiveOrExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInstanceOfExpression; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTMultiplicativeExpression; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTNormalAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; +import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; +import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression; +import net.sourceforge.pmd.lang.java.ast.ASTShiftExpression; +import net.sourceforge.pmd.lang.java.ast.ASTSingleMemberAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTType; +import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.TypeNode; +import net.sourceforge.pmd.lang.java.typeresolution.ClassTypeResolver; +import net.sourceforge.pmd.lang.java.typeresolution.PMDASMClassLoader; + +// +// Helpful reading: +// http://www.janeg.ca/scjp/oper/promotions.html +// http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html +// + +/** + * 1. custom type resolver,fix bug: resolve type of anonymous class failed 2. set anonymous class to parent's type + * + * @author unknown + * @date 2016/11/21 + */ +public class FixClassTypeResolver extends ClassTypeResolver { + + private static final Logger LOG = Logger.getLogger(FixClassTypeResolver.class.getName()); + + private static final Map> PRIMITIVE_TYPES; + private static final Map JAVA_LANG; + private static final String DOT_STRING = "."; + private static final String EXCLAMATION = "!"; + + static { + // Note: Assumption here that primitives come from same parent + // ClassLoader regardless of what ClassLoader we are passed + Map> thePrimitiveTypes = new HashMap<>(); + thePrimitiveTypes.put("void", Void.TYPE); + thePrimitiveTypes.put(boolean.class.getName(), Boolean.TYPE); + thePrimitiveTypes.put(byte.class.getName(), Byte.TYPE); + thePrimitiveTypes.put(char.class.getName(), Character.TYPE); + thePrimitiveTypes.put(short.class.getName(), Short.TYPE); + thePrimitiveTypes.put(int.class.getName(), Integer.TYPE); + thePrimitiveTypes.put(long.class.getName(), Long.TYPE); + thePrimitiveTypes.put(float.class.getName(), Float.TYPE); + thePrimitiveTypes.put(double.class.getName(), Double.TYPE); + PRIMITIVE_TYPES = Collections.unmodifiableMap(thePrimitiveTypes); + + Map theJavaLang = new HashMap<>(); + theJavaLang.put("Boolean", "java.lang.Boolean"); + theJavaLang.put("Byte", "java.lang.Byte"); + theJavaLang.put("Character", "java.lang.Character"); + theJavaLang.put("CharSequence", "java.lang.CharSequence"); + theJavaLang.put("Class", "java.lang.Class"); + theJavaLang.put("ClassLoader", "java.lang.ClassLoader"); + theJavaLang.put("Cloneable", "java.lang.Cloneable"); + theJavaLang.put("Comparable", "java.lang.Comparable"); + theJavaLang.put("Compiler", "java.lang.Compiler"); + theJavaLang.put("Double", "java.lang.Double"); + theJavaLang.put("Float", "java.lang.Float"); + theJavaLang.put("InheritableThreadLocal", "java.lang.InheritableThreadLocal"); + theJavaLang.put("Integer", "java.lang.Integer"); + theJavaLang.put("Long", "java.lang.Long"); + theJavaLang.put("Math", "java.lang.Math"); + theJavaLang.put("Number", "java.lang.Number"); + theJavaLang.put("Object", "java.lang.Object"); + theJavaLang.put("Package", "java.lang.Package"); + theJavaLang.put("Process", "java.lang.Process"); + theJavaLang.put("Runnable", "java.lang.Runnable"); + theJavaLang.put("Runtime", "java.lang.Runtime"); + theJavaLang.put("RuntimePermission", "java.lang.RuntimePermission"); + theJavaLang.put("SecurityManager", "java.lang.SecurityManager"); + theJavaLang.put("Short", "java.lang.Short"); + theJavaLang.put("StackTraceElement", "java.lang.StackTraceElement"); + theJavaLang.put("StrictMath", "java.lang.StrictMath"); + theJavaLang.put("String", "java.lang.String"); + theJavaLang.put("StringBuffer", "java.lang.StringBuffer"); + theJavaLang.put("System", "java.lang.System"); + theJavaLang.put("Thread", "java.lang.Thread"); + theJavaLang.put("ThreadGroup", "java.lang.ThreadGroup"); + theJavaLang.put("ThreadLocal", "java.lang.ThreadLocal"); + theJavaLang.put("Throwable", "java.lang.Throwable"); + theJavaLang.put("Void", "java.lang.Void"); + JAVA_LANG = Collections.unmodifiableMap(theJavaLang); + } + + private final PMDASMClassLoader pmdClassLoader; + private Map importedClasses; + private List importedOnDemand; + private int anonymousClassCounter = 0; + + public FixClassTypeResolver() { + this(FixClassTypeResolver.class.getClassLoader()); + } + + public FixClassTypeResolver(ClassLoader classLoader) { + pmdClassLoader = PMDASMClassLoader.getInstance(classLoader); + } + + // FUTURE ASTCompilationUnit should not be a TypeNode. Clean this up + // accordingly. + @Override + public Object visit(ASTCompilationUnit node, Object data) { + String className = null; + try { + importedOnDemand = new ArrayList<>(); + importedClasses = new HashMap<>(16); + className = getClassName(node); + if (className != null) { + populateClassName(node, className); + } + } catch (ClassNotFoundException | NoClassDefFoundError e) { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "Could not find class " + className + ", due to: " + e); + } + } catch (LinkageError e) { + if (LOG.isLoggable(Level.WARNING)) { + LOG.log(Level.WARNING, "Could not find class " + className + ", due to: " + e); + } + } finally { + populateImports(node); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTImportDeclaration node, Object data) { + ASTName importedType = (ASTName)node.jjtGetChild(0); + if (importedType.getType() != null) { + node.setType(importedType.getType()); + } else { + populateType(node, importedType.getImage()); + } + + if (node.getType() != null) { + node.setPackage(node.getType().getPackage()); + } + return data; + } + + @Override + public Object visit(ASTTypeDeclaration node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTClassOrInterfaceType node, Object data) { + String typeName = node.getImage(); + populateType(node, typeName); + return data; + } + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + populateType(node, node.getImage()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTEnumDeclaration node, Object data) { + populateType(node, node.getImage()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTAnnotationTypeDeclaration node, Object data) { + populateType(node, node.getImage()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTName node, Object data) { + /* + * Only doing this for nodes where getNameDeclaration is null this means + * it's not a named node, i.e. Static reference or Annotation Doing this + * for memory - TODO: Investigate if there is a valid memory concern or + * not + */ + if (node.getNameDeclaration() == null) { + // Skip these scenarios as there is no type to populate in these + // cases: + // 1) Parent is a PackageDeclaration, which is not a type + // 2) Parent is a ImportDeclaration, this is handled elsewhere. + if (!(node.jjtGetParent() instanceof ASTPackageDeclaration + || node.jjtGetParent() instanceof ASTImportDeclaration)) { + String name = node.getImage(); + if (name.indexOf(StringAndCharConstants.DOT) != -1) { + name = name.substring(0, name.indexOf(StringAndCharConstants.DOT)); + } + populateType(node, name); + } + } else { + // Carry over the type from the declaration + if (node.getNameDeclaration().getNode() instanceof TypeNode) { + node.setType(((TypeNode)node.getNameDeclaration().getNode()).getType()); + } + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTFieldDeclaration node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTVariableDeclarator node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTVariableDeclaratorId node, Object data) { + if (node == null || node.getNameDeclaration() == null) { + return super.visit(node, data); + } + String name = node.getNameDeclaration().getTypeImage(); + if (name != null) { + populateType(node, name); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTType node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTReferenceType node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTPrimitiveType node, Object data) { + populateType(node, node.getImage()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTConditionalExpression node, Object data) { + super.visit(node, data); + // noinspection StatementWithEmptyBody + if (node.isTernary()) { + // TODO Rules for Ternary are complex + } else { + rollupTypeUnary(node); + } + return data; + } + + @Override + public Object visit(ASTConditionalOrExpression node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTConditionalAndExpression node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTInclusiveOrExpression node, Object data) { + super.visit(node, data); + rollupTypeBinaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTExclusiveOrExpression node, Object data) { + super.visit(node, data); + rollupTypeBinaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTAndExpression node, Object data) { + super.visit(node, data); + rollupTypeBinaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTEqualityExpression node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTInstanceOfExpression node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTRelationalExpression node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTShiftExpression node, Object data) { + super.visit(node, data); + // Unary promotion on LHS is type of a shift operation + rollupTypeUnaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTAdditiveExpression node, Object data) { + super.visit(node, data); + rollupTypeBinaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTMultiplicativeExpression node, Object data) { + super.visit(node, data); + rollupTypeBinaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTUnaryExpression node, Object data) { + super.visit(node, data); + rollupTypeUnaryNumericPromotion(node); + return data; + } + + @Override + public Object visit(ASTPreIncrementExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTPreDecrementExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) { + super.visit(node, data); + if (EXCLAMATION.equals(node.getImage())) { + populateType(node, boolean.class.getName()); + } else { + rollupTypeUnary(node); + } + return data; + } + + @Override + public Object visit(ASTPostfixExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTCastExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTPrimaryExpression node, Object data) { + super.visit(node, data); + if (node.jjtGetNumChildren() == 1) { + rollupTypeUnary(node); + } else { + // TODO OMG, this is complicated. PrimaryExpression, PrimaryPrefix + // and PrimarySuffix are all related. + } + return data; + } + + @Override + public Object visit(ASTPrimaryPrefix node, Object data) { + super.visit(node, data); + if (node.getImage() == null) { + rollupTypeUnary(node); + } else { + // TODO OMG, this is complicated. PrimaryExpression, PrimaryPrefix + // and PrimarySuffix are all related. + } + return data; + } + + @Override + public Object visit(ASTPrimarySuffix node, Object data) { + super.visit(node, data); + // TODO OMG, this is complicated. PrimaryExpression, PrimaryPrefix and + // PrimarySuffix are all related. + return data; + } + + @Override + public Object visit(ASTNullLiteral node, Object data) { + // No explicit type + return super.visit(node, data); + } + + @Override + public Object visit(ASTBooleanLiteral node, Object data) { + populateType(node, boolean.class.getName()); + return super.visit(node, data); + } + + @Override + public Object visit(ASTLiteral node, Object data) { + super.visit(node, data); + if (node.jjtGetNumChildren() != 0) { + rollupTypeUnary(node); + } else { + if (node.isIntLiteral()) { + populateType(node, int.class.getName()); + } else if (node.isLongLiteral()) { + populateType(node, long.class.getName()); + } else if (node.isFloatLiteral()) { + populateType(node, float.class.getName()); + } else if (node.isDoubleLiteral()) { + populateType(node, double.class.getName()); + } else if (node.isCharLiteral()) { + populateType(node, char.class.getName()); + } else if (node.isStringLiteral()) { + populateType(node, String.class.getName()); + } else { + throw new IllegalStateException("PMD error, unknown literal type!"); + } + } + return data; + } + + @Override + public Object visit(ASTAllocationExpression node, Object data) { + super.visit(node, data); + boolean notRollupTypeUnary = node.jjtGetNumChildren() >= NumberConstants.INTEGER_SIZE_OR_LENGTH_2 + && node.jjtGetChild(1) instanceof ASTArrayDimsAndInits + || node.jjtGetNumChildren() >= NumberConstants.INTEGER_SIZE_OR_LENGTH_3 + && node.jjtGetChild(NumberConstants.INDEX_2) instanceof ASTArrayDimsAndInits; + if (!notRollupTypeUnary) { + rollupTypeUnary(node); + } + return data; + } + + @Override + public Object visit(ASTStatementExpression node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTNormalAnnotation node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTMarkerAnnotation node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + @Override + public Object visit(ASTSingleMemberAnnotation node, Object data) { + super.visit(node, data); + rollupTypeUnary(node); + return data; + } + + /** + * Roll up the type based on type of the first child node. + * + * @param typeNode type node + */ + private void rollupTypeUnary(TypeNode typeNode) { + Node node = typeNode; + if (node.jjtGetNumChildren() >= 1) { + Node child = node.jjtGetChild(0); + if (child instanceof TypeNode) { + typeNode.setType(((TypeNode)child).getType()); + } + } + } + + /** + * Roll up the type based on type of the first child node using Unary + * Numeric Promotion per JLS 5.6.1 + * + * @param typeNode type node + */ + private void rollupTypeUnaryNumericPromotion(TypeNode typeNode) { + Node node = typeNode; + if (node.jjtGetNumChildren() >= 1) { + Node child = node.jjtGetChild(0); + if (child instanceof TypeNode) { + Class type = ((TypeNode)child).getType(); + if (type != null) { + if (byte.class.getName().equals(type.getName()) || short.class.getName().equals(type.getName()) + || char.class.getName().equals(type.getName())) { + populateType(typeNode, int.class.getName()); + } else { + typeNode.setType(((TypeNode)child).getType()); + } + } + } + } + } + + /** + * Roll up the type based on type of the first and second child nodes using + * Binary Numeric Promotion per JLS 5.6.2 + * + * @param typeNode type node + */ + private void rollupTypeBinaryNumericPromotion(TypeNode typeNode) { + Node node = typeNode; + if (node.jjtGetNumChildren() >= NumberConstants.INTEGER_SIZE_OR_LENGTH_2) { + Node child1 = node.jjtGetChild(0); + Node child2 = node.jjtGetChild(1); + if (child1 instanceof TypeNode && child2 instanceof TypeNode) { + Class type1 = ((TypeNode)child1).getType(); + Class type2 = ((TypeNode)child2).getType(); + if (type1 != null && type2 != null) { + // Yeah, String is not numeric, but easiest place to handle + // it, only affects ASTAdditiveExpression + if (String.class.getName().equals(type1.getName()) || String.class.getName().equals( + type2.getName())) { + populateType(typeNode, String.class.getName()); + } else if (boolean.class.getName().equals(type1.getName()) || boolean.class.getName().equals( + type2.getName())) { + populateType(typeNode, boolean.class.getName()); + } else if (double.class.getName().equals(type1.getName()) || double.class.getName().equals( + type2.getName())) { + populateType(typeNode, double.class.getName()); + } else if (float.class.getName().equals(type1.getName()) || float.class.getName().equals( + type2.getName())) { + populateType(typeNode, float.class.getName()); + } else if (long.class.getName().equals(type1.getName()) || long.class.getName().equals( + type2.getName())) { + populateType(typeNode, long.class.getName()); + } else { + populateType(typeNode, int.class.getName()); + } + } else if (type1 != null || type2 != null) { + // If one side is known to be a String, then the result is a + // String + // Yeah, String is not numeric, but easiest place to handle + // it, only affects ASTAdditiveExpression + boolean populateString = type1 != null && String.class.getName().equals(type1.getName()) + || type2 != null && String.class.getName().equals(type2.getName()); + if (populateString) { + populateType(typeNode, String.class.getName()); + } + } + } + } + } + + private void populateType(TypeNode node, String className) { + + String qualifiedName = className; + Class myType = PRIMITIVE_TYPES.get(className); + if (myType == null && importedClasses != null) { + if (importedClasses.containsKey(className)) { + qualifiedName = importedClasses.get(className); + } else if (importedClasses.containsValue(className)) { + qualifiedName = className; + } + if (qualifiedName != null) { + try { + /* + * TODO - the map right now contains just class names. if we + * use a map of classname/class then we don't have to hit + * the class loader for every type - much faster + */ + myType = pmdClassLoader.loadClass(qualifiedName); + } catch (ClassNotFoundException e) { + myType = processOnDemand(qualifiedName); + } catch (NoClassDefFoundError e) { + myType = processOnDemand(qualifiedName); + } catch (LinkageError e) { + myType = processOnDemand(qualifiedName); + } + } + } + if (myType == null && qualifiedName != null && qualifiedName.contains(DOT_STRING)) { + // try if the last part defines a inner class + String qualifiedNameInner = qualifiedName.substring(0, + qualifiedName.lastIndexOf(StringAndCharConstants.DOT)) + "$" + + qualifiedName.substring(qualifiedName.lastIndexOf(StringAndCharConstants.DOT) + 1); + try { + myType = pmdClassLoader.loadClass(qualifiedNameInner); + } catch (Exception e) { + // ignored + } + } + if (myType == null && qualifiedName != null && !qualifiedName.contains(DOT_STRING)) { + // try again with java.lang.... + try { + myType = pmdClassLoader.loadClass("java.lang." + qualifiedName); + } catch (Exception e) { + // ignored + } + } + if (myType != null) { + node.setType(myType); + } + } + + /** + * Check whether the supplied class name exists. + */ + @Override + public boolean classNameExists(String fullyQualifiedClassName) { + try { + pmdClassLoader.loadClass(fullyQualifiedClassName); + // Class found + return true; + } catch (ClassNotFoundException e) { + return false; + } catch (NoClassDefFoundError e) { + return false; + } + } + + @Override + public Class loadClass(String fullyQualifiedClassName) { + try { + return pmdClassLoader.loadClass(fullyQualifiedClassName); + } catch (ClassNotFoundException e) { + return null; + } + } + + private Class processOnDemand(String qualifiedName) { + for (String entry : importedOnDemand) { + try { + return pmdClassLoader.loadClass(entry + "." + qualifiedName); + } catch (Throwable e) { + } + } + return null; + } + + private String getClassName(ASTCompilationUnit node) { + ASTClassOrInterfaceDeclaration classDecl = node.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class); + if (classDecl == null) { + // Happens if this compilation unit only contains an + // enum + return null; + } + if (node.declarationsAreInDefaultPackage()) { + return classDecl.getImage(); + } + ASTPackageDeclaration pkgDecl = node.getPackageDeclaration(); + importedOnDemand.add(pkgDecl.getPackageNameImage()); + return pkgDecl.getPackageNameImage() + DOT_STRING + classDecl.getImage(); + } + + /** + * If the outer class wasn't found then we'll get in here + */ + private void populateImports(ASTCompilationUnit node) { + List theImportDeclarations = node.findChildrenOfType(ASTImportDeclaration.class); + + importedClasses.putAll(JAVA_LANG); + + // go through the imports + for (ASTImportDeclaration anImportDeclaration : theImportDeclarations) { + String strPackage = anImportDeclaration.getPackageName(); + if (anImportDeclaration.isImportOnDemand()) { + importedOnDemand.add(strPackage); + } else if (!anImportDeclaration.isImportOnDemand()) { + String strName = anImportDeclaration.getImportedName(); + importedClasses.put(strName, strName); + importedClasses.put(strName.substring(strPackage.length() + 1), strName); + } + } + } + + private void populateClassName(ASTCompilationUnit node, String className) throws ClassNotFoundException { + node.setType(pmdClassLoader.loadClass(className)); + importedClasses.putAll(pmdClassLoader.getImportedClasses(className)); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/AbstractXpathRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/AbstractXpathRule.java new file mode 100644 index 0000000..8aede72 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/AbstractXpathRule.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang; + +import com.alibaba.p3c.pmd.I18nResources; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.XPathRule; + +/** + * @author caikang + * @date 2017/05/25 + */ +public abstract class AbstractXpathRule extends XPathRule { + @Override + public void setDescription(String description) { + super.setDescription(I18nResources.getMessageWithExceptionHandled(description)); + } + + @Override + public void setMessage(String message) { + super.setMessage(I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message) { + super.addViolationWithMessage(data, node, I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message, Object[] args) { + super.addViolationWithMessage(data, node, + String.format(I18nResources.getMessageWithExceptionHandled(message), args)); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractAliRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractAliRule.java new file mode 100644 index 0000000..743bb71 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractAliRule.java @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.fix.FixClassTypeResolver; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; + +/** + * re calculate node type + * + * @author caikang + * @date 2016/11/20 + */ +public abstract class AbstractAliRule extends AbstractJavaRule { + @Override + public Object visit(ASTCompilationUnit node, Object data) { + FixClassTypeResolver classTypeResolver = new FixClassTypeResolver(AbstractAliRule.class.getClassLoader()); + node.setClassTypeResolver(classTypeResolver); + node.jjtAccept(classTypeResolver, data); + return super.visit(node, data); + } + + @Override + public void setDescription(String description) { + super.setDescription(I18nResources.getMessage(description)); + } + + @Override + public void setMessage(String message) { + super.setMessage(I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message) { + super.addViolationWithMessage(data, node, I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message, Object[] args) { + super.addViolationWithMessage(data, node, + String.format(I18nResources.getMessageWithExceptionHandled(message), args)); + } +} + diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractPojoRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractPojoRule.java new file mode 100644 index 0000000..fb59ee9 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/AbstractPojoRule.java @@ -0,0 +1,69 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.util.PojoUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; + +/** + * Base class for POJO + * + * @author zenghou.fw + * @date 2016/11/25 + */ +public abstract class AbstractPojoRule extends AbstractAliRule { + + /** + * filter for all POJO class,skip if no POJO. + * consider inner class + * + * @param node compilation unit + * @param data rule context + * @return result + */ + @Override + public Object visit(ASTCompilationUnit node, Object data) { + // proceed if contains POJO + if (hasPojoInJavaFile(node)) { + return super.visit(node, data); + } + return data; + } + + /** + * check contains POJO + * @param node compilation unit + * @return + */ + private boolean hasPojoInJavaFile(ASTCompilationUnit node) { + List klasses = node.findDescendantsOfType( + ASTClassOrInterfaceDeclaration.class); + for (ASTClassOrInterfaceDeclaration klass : klasses) { + if (isPojo(klass)) { + return true; + } + } + return false; + } + + protected boolean isPojo(ASTClassOrInterfaceDeclaration node) { + return PojoUtils.isPojo(node); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractAliCommentRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractAliCommentRule.java new file mode 100644 index 0000000..54b5855 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractAliCommentRule.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import com.alibaba.p3c.pmd.I18nResources; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.rule.comments.AbstractCommentRule; + +/** + * @author caikang + * @date 2017/06/21 + */ +public class AbstractAliCommentRule extends AbstractCommentRule { + @Override + public void setDescription(String description) { + super.setDescription(I18nResources.getMessageWithExceptionHandled(description)); + } + + @Override + public void setMessage(String message) { + super.setMessage(I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message) { + super.addViolationWithMessage(data, node, I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message, Object[] args) { + super.addViolationWithMessage(data, node, + String.format(I18nResources.getMessageWithExceptionHandled(message), args)); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractMethodOrInterfaceMethodMustUseJavadocRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractMethodOrInterfaceMethodMustUseJavadocRule.java new file mode 100644 index 0000000..5eb3dfc --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AbstractMethodOrInterfaceMethodMustUseJavadocRule.java @@ -0,0 +1,162 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.List; +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTNameList; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.Comment; +import net.sourceforge.pmd.lang.java.ast.FormalComment; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Abstract methods (including methods in interface) should be commented by Javadoc. + * Javadoc should include method instruction, description of parameters, return values and possible exception. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class AbstractMethodOrInterfaceMethodMustUseJavadocRule extends AbstractAliCommentRule { + + private static final String METHOD_IN_INTERFACE_XPATH = + "./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration"; + private static final String METHOD_VARIABLE_DECLARATOR_XPATH + = "./MethodDeclarator/FormalParameters/FormalParameter/VariableDeclaratorId"; + + private static final String MESSAGE_KEY_PREFIX + = "java.comment.AbstractMethodOrInterfaceMethodMustUseJavadocRule.violation.msg"; + + private static final Pattern EMPTY_CONTENT_PATTERN = Pattern.compile("[/*\\n\\r\\s]+(@.*)?", Pattern.DOTALL); + private static final Pattern RETURN_PATTERN = Pattern.compile(".*@return.*", Pattern.DOTALL); + + @Override + public Object visit(ASTClassOrInterfaceDeclaration decl, Object data) { + if (decl.isAbstract()) { + List methods = decl.findDescendantsOfType(ASTMethodDeclaration.class); + for (ASTMethodDeclaration method : methods) { + if (!method.isAbstract()) { + continue; + } + Comment comment = method.comment(); + if (null == comment || !(comment instanceof FormalComment)) { + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".abstract", + method.getMethodName())); + } else { + this.checkMethodCommentFormat(method, data); + } + } + } + if (!decl.isInterface()) { + return super.visit(decl, data); + } + List methodNodes; + try { + methodNodes = decl.findChildNodesWithXPath(METHOD_IN_INTERFACE_XPATH); + } catch (JaxenException e) { + throw new RuntimeException("XPath expression " + METHOD_IN_INTERFACE_XPATH + + " failed: " + e.getLocalizedMessage(), e); + } + + for (Node node : methodNodes) { + ASTMethodDeclaration method = (ASTMethodDeclaration)node; + Comment comment = method.comment(); + if (null == comment || !(comment instanceof FormalComment)) { + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".interface", + method.getMethodName())); + } else { + this.checkMethodCommentFormat(method, data); + } + } + return super.visit(decl, data); + } + + public void checkMethodCommentFormat(ASTMethodDeclaration method, Object data) { + Comment comment = method.comment(); + String commentContent = comment.getImage(); + + // method instruction + if (EMPTY_CONTENT_PATTERN.matcher(commentContent).matches()) { + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".desc", + method.getMethodName())); + } + + // description of parameters + List variableDeclaratorIds; + try { + variableDeclaratorIds = method.findChildNodesWithXPath(METHOD_VARIABLE_DECLARATOR_XPATH); + } catch (JaxenException e) { + throw new RuntimeException( + "XPath expression " + METHOD_VARIABLE_DECLARATOR_XPATH + " failed: " + e.getLocalizedMessage(), e); + } + + for (Node variableDeclaratorId : variableDeclaratorIds) { + ASTVariableDeclaratorId param = (ASTVariableDeclaratorId)variableDeclaratorId; + String paramName = param.getImage(); + Pattern paramNamePattern = Pattern.compile(".*@param\\s+" + paramName + ".*", Pattern.DOTALL); + + if (!paramNamePattern.matcher(commentContent).matches()) { + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".parameter", + method.getMethodName(), paramName)); + } + } + + // return values + if (!method.isVoid() && !RETURN_PATTERN.matcher(commentContent).matches()) { + + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".return", + method.getMethodName())); + } + + // possible exception + ASTNameList nameList = method.getThrows(); + if (null != nameList) { + List exceptions = nameList.findDescendantsOfType(ASTName.class); + for (ASTName exception : exceptions) { + String exceptionName = exception.getImage(); + Pattern exceptionPattern = Pattern.compile(".*@throws\\s+" + + exceptionName + ".*", Pattern.DOTALL); + + if (!exceptionPattern.matcher(commentContent).matches()) { + ViolationUtils.addViolationWithPrecisePosition(this, method, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".exception", + method.getMethodName(), exceptionName)); + } + } + } + } + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + assignCommentsToDeclarations(cUnit); + return super.visit(cUnit, data); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AvoidCommentBehindStatementRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AvoidCommentBehindStatementRule.java new file mode 100644 index 0000000..56ac4d8 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/AvoidCommentBehindStatementRule.java @@ -0,0 +1,95 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.List; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.util.CommentUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.java.ast.Comment; + +/** + * [Mandatory] Single line comments in a method should be put above the code to be commented, by using // and + * multiple lines by using \/* *\/. Alignment for comments should be noticed carefully. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class AvoidCommentBehindStatementRule extends AbstractAliCommentRule { + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + SortedMap itemsByLineNumber = orderedCommentsAndExpressions(cUnit); + AbstractJavaNode lastNode = null; + + for (Entry entry : itemsByLineNumber.entrySet()) { + Node value = entry.getValue(); + if (value instanceof AbstractJavaNode) { + lastNode = (AbstractJavaNode)value; + } else if (value instanceof Comment) { + Comment comment = (Comment)value; + if (lastNode != null && (comment.getBeginLine() == lastNode.getBeginLine()) + && (comment.getEndColumn() > lastNode.getBeginColumn())) { + addViolationWithMessage(data, lastNode, + I18nResources.getMessage("java.comment.AvoidCommentBehindStatementRule.violation.msg"), + comment.getBeginLine(), comment.getEndLine()); + } + } + } + + return super.visit(cUnit, data); + } + + /** + * Check comments behind nodes. + * + * @param cUnit compilation unit + * @return sorted comments and expressions + */ + protected SortedMap orderedCommentsAndExpressions(ASTCompilationUnit cUnit) { + + SortedMap itemsByLineNumber = new TreeMap<>(); + + // expression nodes + List expressionNodes = cUnit.findDescendantsOfType(ASTExpression.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, expressionNodes); + + // filed declaration nodes + List fieldNodes = + cUnit.findDescendantsOfType(ASTFieldDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, fieldNodes); + + // enum constant nodes + List enumConstantNodes = + cUnit.findDescendantsOfType(ASTEnumConstant.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, enumConstantNodes); + + CommentUtils.addNodesToSortedMap(itemsByLineNumber, cUnit.getComments()); + + return itemsByLineNumber; + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/ClassMustHaveAuthorRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/ClassMustHaveAuthorRule.java new file mode 100644 index 0000000..e5b5c2b --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/ClassMustHaveAuthorRule.java @@ -0,0 +1,103 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.java.ast.Comment; + +/** + * [Mandatory] Every class should include information of author(s) and date. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class ClassMustHaveAuthorRule extends AbstractAliCommentRule { + + private static final Pattern AUTHOR_PATTERN = Pattern.compile(".*@author.*", Pattern.DOTALL); + + private static final String MESSAGE_KEY_PREFIX = "java.comment.ClassMustHaveAuthorRule.violation.msg"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration decl, Object data) { + // Exclude nested classes + if (decl.isNested()) { + return super.visit(decl, data); + } + + // Exclude inner classes + if (!decl.isPublic()) { + return super.visit(decl, data); + } + + checkAuthorComment(decl, data); + + return super.visit(decl, data); + } + + @Override + public Object visit(ASTEnumDeclaration decl, Object data) { + // Exclude inner enum + if (!decl.isPublic()) { + return super.visit(decl, data); + } + + // Inner enum should have author tag in outer class. + ASTClassOrInterfaceDeclaration parent = decl.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); + if (parent != null) { + return super.visit(decl, data); + } + + checkAuthorComment(decl, data); + + return super.visit(decl, data); + } + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + assignCommentsToDeclarations(cUnit); + + return super.visit(cUnit, data); + } + + /** + * Check if node's comment contains author tag. + * + * @param decl node + * @param data ruleContext + */ + public void checkAuthorComment(AbstractJavaNode decl, Object data) { + Comment comment = decl.comment(); + if (null == comment) { + ViolationUtils.addViolationWithPrecisePosition(this, decl, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".comment", decl.getImage())); + } else { + String commentContent = comment.getImage(); + boolean hasAuthor = AUTHOR_PATTERN.matcher(commentContent).matches(); + if (!hasAuthor) { + ViolationUtils.addViolationWithPrecisePosition(this, decl, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".author", decl.getImage())); + } + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentsMustBeJavadocFormatRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentsMustBeJavadocFormatRule.java new file mode 100644 index 0000000..5301c2c --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentsMustBeJavadocFormatRule.java @@ -0,0 +1,224 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.util.CommentUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaAccessNode; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.java.ast.Comment; +import net.sourceforge.pmd.lang.java.ast.MultiLineComment; +import net.sourceforge.pmd.lang.java.ast.SingleLineComment; +import net.sourceforge.pmd.lang.java.ast.Token; +import org.apache.commons.lang3.StringUtils; + +/** + * [Mandatory] Javadoc should be used for classes, class variables and methods. + * The format should be '\/** comment **\/', rather than '// xxx'. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class CommentsMustBeJavadocFormatRule extends AbstractAliCommentRule { + + private static final String MESSAGE_KEY_PREFIX = "java.comment.CommentsMustBeJavadocFormatRule.violation.msg"; + + @Override + public Object visit(final ASTClassOrInterfaceDeclaration decl, Object data) { + checkComment(decl, data, new MessageMaker() { + @Override + public String make() { + return I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".class", + decl.getImage()); + } + }); + return super.visit(decl, data); + } + + @Override + public Object visit(final ASTConstructorDeclaration decl, Object data) { + checkComment(decl, data, new MessageMaker() { + @Override + public String make() { + String constructorName = ((Token)decl.jjtGetFirstToken()).image; + if (decl.getParameters().getParameterCount() == 0) { + return I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".constructor.default", + constructorName); + } + List formalParameters = decl.getParameters() + .findChildrenOfType(ASTFormalParameter.class); + List strings = new ArrayList<>(formalParameters.size()); + + for (ASTFormalParameter formalParameter : formalParameters) { + strings.add(formalParameter.jjtGetFirstToken().toString() + " " + + formalParameter.jjtGetLastToken().toString()); + } + return I18nResources + .getMessage(MESSAGE_KEY_PREFIX + ".constructor.parameter", + constructorName, + StringUtils.join(strings, ",")); + } + }); + return super.visit(decl, data); + } + + @Override + public Object visit(final ASTMethodDeclaration decl, Object data) { + checkComment(decl, data, new MessageMaker() { + @Override + public String make() { + return I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".method", + decl.getMethodName()); + } + }); + return super.visit(decl, data); + } + + @Override + public Object visit(final ASTFieldDeclaration decl, Object data) { + checkComment(decl, data, new MessageMaker() { + @Override + public String make() { + return I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".field", + decl.getVariableName()); + } + }); + return super.visit(decl, data); + } + + @Override + public Object visit(final ASTEnumDeclaration decl, Object data) { + checkComment(decl, data, new MessageMaker() { + @Override + public String make() { + return I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".enum", + decl.getImage()); + } + }); + return super.visit(decl, data); + } + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + assignCommentsToDeclarations(cUnit); + + return super.visit(cUnit, data); + } + + private void checkComment(AbstractJavaAccessNode decl, Object data, MessageMaker maker) { + Comment comment = decl.comment(); + if (comment instanceof SingleLineComment || comment instanceof MultiLineComment) { + addViolationWithMessage(data, decl, + maker.make(), comment.getBeginLine(), comment.getEndLine()); + } + } + + @Override + protected void assignCommentsToDeclarations(ASTCompilationUnit cUnit) { + + SortedMap itemsByLineNumber = orderedComments(cUnit); + Comment lastComment = null; + AbstractJavaNode lastNode = null; + + for (Entry entry : itemsByLineNumber.entrySet()) { + Node value = entry.getValue(); + + if (value instanceof AbstractJavaNode) { + AbstractJavaNode node = (AbstractJavaNode)value; + + // Check if comment is one line above class, field, method. + if (lastComment != null && isCommentOneLineBefore(lastComment, lastNode, node)) { + node.comment(lastComment); + lastComment = null; + } + + lastNode = node; + } else if (value instanceof Comment) { + lastComment = (Comment)value; + } + } + } + + protected SortedMap orderedComments(ASTCompilationUnit cUnit) { + + SortedMap itemsByLineNumber = new TreeMap<>(); + + CommentUtils.addNodesToSortedMap(itemsByLineNumber, cUnit.getComments()); + + List classDecl = + cUnit.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, classDecl); + + List fields = cUnit.findDescendantsOfType(ASTFieldDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, fields); + + List methods = cUnit.findDescendantsOfType(ASTMethodDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, methods); + + List constructors = cUnit.findDescendantsOfType(ASTConstructorDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, constructors); + + List enumDecl = cUnit.findDescendantsOfType(ASTEnumDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, enumDecl); + + return itemsByLineNumber; + } + + private boolean isCommentOneLineBefore(Comment lastComment, Node lastNode, Node node) { + ASTClassOrInterfaceBodyDeclaration parentClass = + node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class); + + // Skip comments inside inner class. + if (parentClass != null && parentClass.isAnonymousInnerClass()) { + return false; + } + + // Skip comments behind nodes. + if (lastNode != null && lastNode.getEndLine() == lastComment.getEndLine()) { + return false; + } + + return lastComment.getEndLine() + 1 == node.getBeginLine(); + } + + /** + * Generate rule violation message. + */ + interface MessageMaker { + /** + * Generate violation message. + * + * @return message + */ + String make(); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/EnumConstantsMustHaveCommentRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/EnumConstantsMustHaveCommentRule.java new file mode 100644 index 0000000..020189c --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/EnumConstantsMustHaveCommentRule.java @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.List; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.util.CommentUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; +import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; + +/** + * [Mandatory] All enumeration type fields should be commented as Javadoc style. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class EnumConstantsMustHaveCommentRule extends AbstractAliCommentRule { + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + SortedMap itemsByLineNumber = this.orderedCommentsAndEnumDeclarations(cUnit); + + // Check comments between ASTEnumDeclaration and ASTEnumConstant. + boolean isPreviousEnumDecl = false; + + for (Entry entry : itemsByLineNumber.entrySet()) { + Node value = entry.getValue(); + + if (value instanceof ASTEnumDeclaration) { + isPreviousEnumDecl = true; + } else if (value instanceof ASTEnumConstant && isPreviousEnumDecl) { + addViolationWithMessage(data, value, + I18nResources.getMessage("java.comment.EnumConstantsMustHaveCommentRule.violation.msg", + value.getImage())); + isPreviousEnumDecl = false; + } else { + isPreviousEnumDecl = false; + } + } + + return super.visit(cUnit, data); + } + + private SortedMap orderedCommentsAndEnumDeclarations(ASTCompilationUnit cUnit) { + SortedMap itemsByLineNumber = new TreeMap<>(); + + List enumDecl = cUnit.findDescendantsOfType(ASTEnumDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, enumDecl); + + List contantDecl = cUnit.findDescendantsOfType(ASTEnumConstant.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, contantDecl); + + CommentUtils.addNodesToSortedMap(itemsByLineNumber, cUnit.getComments()); + + return itemsByLineNumber; + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/RemoveCommentedCodeRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/RemoveCommentedCodeRule.java new file mode 100644 index 0000000..0b00205 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/comment/RemoveCommentedCodeRule.java @@ -0,0 +1,186 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import java.util.List; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.lang.java.rule.util.CommentUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.Comment; +import net.sourceforge.pmd.lang.java.ast.JavaNode; + +/** + * [Recommended] Codes or configuration that is noticed to be obsoleted should be resolutely removed from projects. + * + * @author keriezhang + * @date 2017/04/14 + */ +public class RemoveCommentedCodeRule extends AbstractAliCommentRule { + + private static final Pattern SUPPRESS_PATTERN = Pattern.compile("\\s*///.*", Pattern.DOTALL); + + private static final Pattern IMPORT_PATTERN = Pattern.compile(".*import\\s(static\\s)?(\\w*\\.)*\\w*;.*", + Pattern.DOTALL); + + private static final Pattern FIELD_PATTERN = Pattern.compile(".*private\\s+(\\w*)\\s+(\\w*);.*", Pattern.DOTALL); + + private static final Pattern METHOD_PATTERN = Pattern.compile( + ".*(public|protected|private)\\s+\\w+\\s+\\w+\\(.*\\)\\s+\\{.*", Pattern.DOTALL); + + /** + * If string matches format ".xxx(.*);\n", then mark it as code. + */ + private static final Pattern STATEMENT_PATTERN = Pattern.compile(".*\\.\\w+\\(.*\\);\n.*", Pattern.DOTALL); + + @Override + public Object visit(ASTCompilationUnit cUnit, Object data) { + checkCommentsBetweenDeclarations(cUnit, data); + + return super.visit(cUnit, data); + } + + protected void checkCommentsBetweenDeclarations(ASTCompilationUnit cUnit, Object data) { + + SortedMap itemsByLineNumber = orderedCommentsAndDeclarations(cUnit); + Comment lastComment = null; + boolean suppressWarning = false; + CommentPatternEnum commentPattern = CommentPatternEnum.NONE; + + for (Entry entry : itemsByLineNumber.entrySet()) { + Node value = entry.getValue(); + + if (value instanceof JavaNode) { + JavaNode node = (JavaNode)value; + + // add violation on the node after comment. + if (lastComment != null && isCommentBefore(lastComment, node)) { + // find code comment, but need filter some case. + if (!CommentPatternEnum.NONE.equals(commentPattern)) { + // check statement pattern only in method + boolean statementOutsideMethod = CommentPatternEnum.STATEMENT.equals(commentPattern) + && !(node instanceof ASTBlockStatement); + if (!statementOutsideMethod) { + addViolationWithMessage(data, node, getMessage(), + lastComment.getBeginLine(), + lastComment.getEndLine()); + } + } + lastComment = null; + } + + // reset data after each node. + suppressWarning = false; + commentPattern = CommentPatternEnum.NONE; + + } else if (value instanceof Comment) { + lastComment = (Comment)value; + String content = lastComment.getImage(); + + if (!suppressWarning) { + suppressWarning = SUPPRESS_PATTERN.matcher(content).matches(); + } + + if (!suppressWarning && CommentPatternEnum.NONE.equals(commentPattern)) { + commentPattern = this.scanCommentedCode(content); + } + } + } + } + + /** + * Common Situations, check in following order: + * 1. commented import + * 2. commented field + * 3. commented method + * 4. commented statement + * + * @param content comment content + * @return check result + */ + protected CommentPatternEnum scanCommentedCode(String content) { + CommentPatternEnum pattern = CommentPatternEnum.NONE; + + if (IMPORT_PATTERN.matcher(content).matches()) { + pattern = CommentPatternEnum.IMPORT; + } else if (FIELD_PATTERN.matcher(content).matches()) { + pattern = CommentPatternEnum.FIELD; + } else if (METHOD_PATTERN.matcher(content).matches()) { + pattern = CommentPatternEnum.METHOD; + } else if (STATEMENT_PATTERN.matcher(content).matches()) { + pattern = CommentPatternEnum.STATEMENT; + } + + return pattern; + } + + @Override + protected SortedMap orderedCommentsAndDeclarations(ASTCompilationUnit cUnit) { + SortedMap itemsByLineNumber = new TreeMap<>(); + + List importDecl = cUnit + .findDescendantsOfType(ASTImportDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, importDecl); + + List classDecl = cUnit + .findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, classDecl); + + List fields = cUnit.findDescendantsOfType(ASTFieldDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, fields); + + List methods = cUnit.findDescendantsOfType(ASTMethodDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, methods); + + List constructors = cUnit.findDescendantsOfType(ASTConstructorDeclaration.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, constructors); + + List blockStatements = cUnit.findDescendantsOfType(ASTBlockStatement.class); + CommentUtils.addNodesToSortedMap(itemsByLineNumber, blockStatements); + + CommentUtils.addNodesToSortedMap(itemsByLineNumber, cUnit.getComments()); + + return itemsByLineNumber; + } + + private boolean isCommentBefore(Comment n1, Node n2) { + return n1.getEndLine() < n2.getBeginLine() || n1.getEndLine() == n2.getBeginLine() + && n1.getEndColumn() < n2.getBeginColumn(); + } + + enum CommentPatternEnum { + /** + * comment has code pattern + */ + IMPORT, + FIELD, + METHOD, + STATEMENT, + NONE + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java new file mode 100644 index 0000000..4a59a93 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidCallStaticSimpleDateFormatRule.java @@ -0,0 +1,180 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.text.SimpleDateFormat; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.locks.Lock; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.dfa.DataFlowNode; +import net.sourceforge.pmd.lang.dfa.StartOrEndDataFlowNode; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.java.ast.Token; + +/** + * [Mandatory] SimpleDataFormat is unsafe, do not define it as a static variable. + * If have to, lock or DateUtils class must be used. + * + * @author caikang + * @date 2016/11/25 + */ +public class AvoidCallStaticSimpleDateFormatRule extends AbstractAliRule { + private static final String FORMAT_METHOD_NAME = "format"; + private static final String LOCK_NAME = "lock"; + private static final String UN_LOCK_NAME = "unlock"; + + @Override + public Object visit(ASTMethodDeclaration node, Object data) { + if (node.isSynchronized()) { + return super.visit(node, data); + } + + handleMethod(node, data); + return super.visit(node, data); + } + + private void handleMethod(ASTMethodDeclaration methodDeclaration, Object data) { + DataFlowNode dataFlowNode = methodDeclaration.getDataFlowNode(); + if (dataFlowNode == null || dataFlowNode.getFlow() == null) { + return; + } + // records of violations,lock block excepted + Stack stack = new Stack<>(); + Set localSimpleDateFormatNames = new HashSet<>(); + for (DataFlowNode flowNode : dataFlowNode.getFlow()) { + handleFlowNode(stack, localSimpleDateFormatNames, flowNode); + } + while (!stack.isEmpty()) { + Node node = stack.pop(); + if (node instanceof ASTPrimaryExpression) { + addViolationWithMessage(data, node, + "java.concurrent.AvoidCallStaticSimpleDateFormatRule.violation.msg", + new Object[] {getExpressName((ASTPrimaryExpression)node)}); + } + } + } + + private void handleFlowNode(Stack stack, Set localSimpleDateFormatNames, DataFlowNode flowNode) { + if (flowNode instanceof StartOrEndDataFlowNode || flowNode.getNode() instanceof ASTMethodDeclaration) { + return; + } + // collect local variables of type SimpleDateFormat if match,then return + if (flowNode.getNode() instanceof ASTVariableDeclarator) { + ASTVariableDeclarator variableDeclarator = (ASTVariableDeclarator)flowNode.getNode(); + if (variableDeclarator.getType() == SimpleDateFormat.class) { + ASTVariableDeclaratorId variableDeclaratorId = + variableDeclarator.getFirstChildOfType(ASTVariableDeclaratorId.class); + localSimpleDateFormatNames.add(variableDeclaratorId.getImage()); + return; + } + } + + if (flowNode.getNode() instanceof ASTStatementExpression) { + ASTStatementExpression statementExpression = (ASTStatementExpression)flowNode.getNode(); + if (isLockStatementExpression(statementExpression)) { + // add lock node + stack.push(flowNode.getNode()); + return; + } else if (isUnLockStatementExpression(statementExpression)) { + // remove element in lock block + while (!stack.isEmpty()) { + Node node = stack.pop(); + if (isLockNode(node)) { + break; + } + } + return; + } + } + AbstractJavaNode javaNode = (AbstractJavaNode)flowNode.getNode(); + ASTPrimaryExpression flowPrimaryExpression = javaNode.getFirstDescendantOfType(ASTPrimaryExpression.class); + if (flowPrimaryExpression == null) { + return; + } + if (flowPrimaryExpression.getFirstParentOfType(ASTSynchronizedStatement.class) != null) { + return; + } + if (!isStaticSimpleDateFormatCall(flowPrimaryExpression, localSimpleDateFormatNames)) { + return; + } + // add violation element (include those in lock block,until we meet unlock block,we can remove them) + stack.push(flowPrimaryExpression); + } + + private String getExpressName(ASTPrimaryExpression primaryExpression) { + ASTName name = primaryExpression.getFirstDescendantOfType(ASTName.class); + return name.getImage(); + } + + private boolean isLockNode(Node node) { + if (!(node instanceof ASTStatementExpression)) { + return false; + } + ASTStatementExpression statementExpression = (ASTStatementExpression)node; + return isLockStatementExpression(statementExpression); + } + + private boolean isStaticSimpleDateFormatCall(ASTPrimaryExpression primaryExpression, + Set localSimpleDateFormatNames) { + if (primaryExpression.jjtGetNumChildren() == 0) { + return false; + } + ASTName name = primaryExpression.getFirstDescendantOfType(ASTName.class); + if (name == null || name.getType() != SimpleDateFormat.class) { + return false; + } + if (localSimpleDateFormatNames.contains(name.getNameDeclaration().getName())) { + return false; + } + ASTPrimaryPrefix primaryPrefix = (ASTPrimaryPrefix)primaryExpression.jjtGetChild(0); + if (primaryPrefix.getType() != SimpleDateFormat.class) { + return false; + } + + Token token = (Token)primaryPrefix.jjtGetLastToken(); + return FORMAT_METHOD_NAME.equals(token.image); + } + + private boolean isLockStatementExpression(ASTStatementExpression statementExpression) { + return isLockTypeAndMethod(statementExpression, LOCK_NAME); + } + + private boolean isUnLockStatementExpression(ASTStatementExpression statementExpression) { + return isLockTypeAndMethod(statementExpression, UN_LOCK_NAME); + } + + private boolean isLockTypeAndMethod(ASTStatementExpression statementExpression, String methodName) { + ASTName name = statementExpression.getFirstDescendantOfType(ASTName.class); + if (name == null || name.getType() == null || !Lock.class.isAssignableFrom(name.getType())) { + return false; + } + Token token = (Token)name.jjtGetLastToken(); + return methodName.equals(token.image); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidConcurrentCompetitionRandomRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidConcurrentCompetitionRandomRule.java new file mode 100644 index 0000000..0608474 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidConcurrentCompetitionRandomRule.java @@ -0,0 +1,130 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.List; +import java.util.Random; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTExtendsList; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import org.jaxen.JaxenException; + +/** + * 6.13 [Recommended] Avoid using Random instance by multiple threads. + * Although it is safe to share this instance, competition on the same seed will damage performance. + * Note: Random instance includes instances of java.util.Random and Math.random(). + * + * @author caikang + * @date 2017/03/29 + */ +public class AvoidConcurrentCompetitionRandomRule extends AbstractAliRule { + + private static final String XPATH_TPL = "//StatementExpression/PrimaryExpression" + + "/PrimaryPrefix/Name[starts-with(@Image,'%s.')]"; + + private static final String MATH_RANDOM_METHOD = ".random"; + + private static final String MESSAGE_KEY_PREFIX = "java.concurrent.AvoidConcurrentCompetitionRandomRule"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + ASTExtendsList extendsList = node.getFirstChildOfType(ASTExtendsList.class); + if (extendsList == null) { + return super.visit(node, data); + } + if (!hasThread(extendsList)) { + return super.visit(node, data); + } + List methodDeclarations = node.findDescendantsOfType(ASTMethodDeclaration.class); + if (methodDeclarations == null || methodDeclarations.isEmpty()) { + return super.visit(node, data); + } + checkMathRandom(methodDeclarations, data); + + List fieldDeclarations = node.findDescendantsOfType(ASTFieldDeclaration.class); + if (fieldDeclarations == null || fieldDeclarations.isEmpty()) { + return super.visit(node, data); + } + for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { + if (fieldDeclaration.getType() == Random.class + && fieldDeclaration.isStatic()) { + checkRandom(fieldDeclaration, methodDeclarations, data); + } + } + return super.visit(node, data); + } + + private void checkMathRandom(List methodDeclarations, Object data) { + for (ASTMethodDeclaration methodDeclaration : methodDeclarations) { + List primaryPrefixes + = methodDeclaration.findDescendantsOfType(ASTPrimaryPrefix.class); + if (primaryPrefixes == null || primaryPrefixes.isEmpty()) { + continue; + } + for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) { + if (primaryPrefix.getType() != Math.class) { + continue; + } + ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); + if (name == null || name.getImage() == null || !name.getImage().endsWith(MATH_RANDOM_METHOD)) { + continue; + } + addViolationWithMessage(data, primaryPrefix, + MESSAGE_KEY_PREFIX + ".violation.msg.math.random"); + } + } + } + + private void checkRandom(ASTFieldDeclaration fieldDeclaration, List methodDeclarations, + Object data) { + for (ASTMethodDeclaration methodDeclaration : methodDeclarations) { + try { + List nodes = methodDeclaration.findChildNodesWithXPath(String.format(XPATH_TPL, + fieldDeclaration.getVariableName())); + if (nodes == null || nodes.isEmpty()) { + continue; + } + for (Node rvNode : nodes) { + addViolationWithMessage(data, rvNode, + MESSAGE_KEY_PREFIX + ".violation.msg.random", + new Object[] {rvNode.getImage()}); + } + } catch (JaxenException ignore) { + } + } + } + + private boolean hasThread(ASTExtendsList extendsList) { + List typeList = extendsList.findChildrenOfType(ASTClassOrInterfaceType.class); + if (typeList == null || typeList.isEmpty()) { + return false; + } + for (ASTClassOrInterfaceType type : typeList) { + if (type.getType() == Thread.class) { + return true; + } + } + return false; + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java new file mode 100644 index 0000000..b056ab6 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidManuallyCreateThreadRule.java @@ -0,0 +1,170 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.List; +import java.util.concurrent.ThreadFactory; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; +import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter; +import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; +import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; +import net.sourceforge.pmd.lang.java.ast.ASTInitializer; +import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTResultType; +import net.sourceforge.pmd.lang.java.ast.ASTType; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.Token; + +/** + * [Mandatory] Threads should be provided by thread pools. Explicitly creating threads is not allowed. + * Note: Using thread pool can reduce the time of creating and destroying thread and save system resource. + * If we do not use thread pools, lots of similar threads will be created which lead to + * "running out of memory" or over-switching problems. + * + * Detection rule + * New Thread can only be created in ThreadFactory.newThread method,as Runtime.getRuntime().addShutdownHook() parameter, + * or in static block + * + * @author caikang + * @date 2016/11/15 + * @see ThreadShouldSetNameRule + */ +public class AvoidManuallyCreateThreadRule extends AbstractAliRule { + + private static final String METHOD_NEW_THREAD = "newThread"; + + @Override + public Object visit(ASTAllocationExpression node, Object data) { + if (node.getType() != Thread.class) { + return super.visit(node, data); + } + if (isAddShutdownHook(node) || isInStaticInitializer(node)) { + return super.visit(node, data); + } + //Allocation in lambda block is ignored + if (node.getFirstParentOfType(ASTLambdaExpression.class) != null) { + return super.visit(node, data); + } + ASTFieldDeclaration fieldDeclaration = node.getFirstParentOfType(ASTFieldDeclaration.class); + //field declaration with thread allocated + if (fieldDeclaration != null && fieldDeclaration.getType() == Thread.class) { + return addViolationAndReturn(node, data); + } + //Declare thread factory field use lambda + if (node.getDataFlowNode() == null && node.getFirstParentOfType(ASTLambdaExpression.class) != null) { + if (fieldDeclaration == null || fieldDeclaration.getType() != ThreadFactory.class) { + return addViolationAndReturn(node, data); + } + return super.visit(node, data); + } + + //in newThread(Runnable) method is ok + if (isInNewThreadMethod(node)) { + return super.visit(node, data); + } + //implements of ThreadFactory + boolean isThreadFactory = (checkForNamingClass(node) || threadFactoryVariable(node)) + && isInPrimaryOrProtectedMethod(node); + if (isThreadFactory) { + return super.visit(node, data); + } + return addViolationAndReturn(node, data); + } + + private boolean isAddShutdownHook(ASTAllocationExpression node) { + ASTBlockStatement blockStatement = node.getFirstParentOfType(ASTBlockStatement.class); + if (blockStatement == null) { + return false; + } + Token token = (Token)blockStatement.jjtGetFirstToken(); + return Runtime.class.getSimpleName().equals(token.image); + } + + private boolean isInStaticInitializer(ASTAllocationExpression node) { + ASTInitializer initializer = node.getFirstParentOfType(ASTInitializer.class); + return initializer != null && initializer.isStatic(); + } + + private boolean threadFactoryVariable(ASTAllocationExpression node) { + ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); + if (methodDeclaration == null) { + return false; + } + ASTVariableDeclarator variableDeclarator = methodDeclaration.getFirstParentOfType(ASTVariableDeclarator.class); + return variableDeclarator != null && variableDeclarator.getType() == ThreadFactory.class; + } + + private boolean isInNewThreadMethod(ASTAllocationExpression node) { + ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); + if (methodDeclaration == null) { + return false; + } + if (!returnThread(methodDeclaration)) { + return false; + } + if (!METHOD_NEW_THREAD.equals(methodDeclaration.getMethodName())) { + return false; + } + List parameters = methodDeclaration.getFirstDescendantOfType(ASTFormalParameters.class) + .findChildrenOfType(ASTFormalParameter.class); + return parameters.size() == 1 + && parameters.get(0).getFirstChildOfType(ASTType.class).getType() == Runnable.class; + } + + private boolean isInPrimaryOrProtectedMethod(ASTAllocationExpression node) { + ASTMethodDeclaration methodDeclaration = node.getFirstParentOfType(ASTMethodDeclaration.class); + return methodDeclaration != null && returnThread(methodDeclaration) && (methodDeclaration.isPrivate() + || methodDeclaration.isProtected()); + } + + private boolean returnThread(ASTMethodDeclaration methodDeclaration) { + ASTResultType resultType = methodDeclaration.getFirstChildOfType(ASTResultType.class); + ASTType type = resultType.getFirstChildOfType(ASTType.class); + return type != null && type.getType() == Thread.class; + } + + private Object addViolationAndReturn(ASTAllocationExpression node, Object data) { + addViolationWithMessage(data, node, "java.concurrent.AvoidManuallyCreateThreadRule.violation.msg"); + return super.visit(node, data); + } + + private boolean checkForNamingClass(ASTAllocationExpression node) { + ASTClassOrInterfaceDeclaration classOrInterfaceDeclaration = + node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); + if (classOrInterfaceDeclaration == null) { + return false; + } + ASTImplementsList implementsList = classOrInterfaceDeclaration.getFirstChildOfType(ASTImplementsList.class); + if (implementsList == null) { + return false; + } + List interfaceTypes = implementsList.findChildrenOfType(ASTClassOrInterfaceType.class); + for (ASTClassOrInterfaceType type : interfaceTypes) { + if (type.getType() == ThreadFactory.class) { + return true; + } + } + return false; + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidUseTimerRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidUseTimerRule.java new file mode 100644 index 0000000..4eb4ccc --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/AvoidUseTimerRule.java @@ -0,0 +1,55 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.Timer; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaTypeNode; + +/** + * [Mandatory] Run multiple TimeTask by using ScheduledExecutorService rather than Timer + * because Timer will kill all running threads in case of failing to catch exception. + * + * @author caikang + * @date 2016/11/15 + */ +public class AvoidUseTimerRule extends AbstractAliRule { + @Override + public Object visit(ASTVariableDeclarator node, Object data) { + checkType(node, data); + return super.visit(node, data); + } + + @Override + public Object visit(ASTPrimaryExpression node, Object data) { + ASTVariableDeclarator variableDeclarator = node.getFirstParentOfType(ASTVariableDeclarator.class); + if (variableDeclarator != null && variableDeclarator.getType() == Timer.class) { + return super.visit(node, data); + } + checkType(node, data); + return super.visit(node, data); + } + + private void checkType(AbstractJavaTypeNode node, Object data) { + if (node.getType() == Timer.class) { + addViolationWithMessage(data, node,"java.concurrent.AvoidUseTimerRule.violation.msg"); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/CountDownShouldInFinallyRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/CountDownShouldInFinallyRule.java new file mode 100644 index 0000000..022e1ab --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/CountDownShouldInFinallyRule.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; +import org.jaxen.JaxenException; + +/** + * [Recommended] When using CountDownLatch to convert asynchronous operations to synchronous ones, + * each thread must call countdown method before quitting. + * Make sure to catch any exception during thread running, to let countdown method be executed. + * If main thread cannot reach await method, program will return until timeout. + * + * Note: Be careful, exception thrown by sub-thread cannot be caught by main thread. + * + * @author caikang + * @date 2017/03/29 + */ +public class CountDownShouldInFinallyRule extends AbstractAliRule { + private static final String XPATH = "./Block/BlockStatement/Statement/StatementExpression" + + "/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image,'.countDown')]"; + + @Override + public Object visit(ASTTryStatement node, Object data) { + try { + List nodes = node.findChildNodesWithXPath(XPATH); + if (nodes == null || nodes.isEmpty()) { + return super.visit(node, data); + } + for (Node nameNode : nodes) { + if (!(nameNode instanceof ASTName)) { + continue; + } + ASTName name = (ASTName)nameNode; + if (name.getType() != CountDownLatch.class) { + continue; + } + addViolationWithMessage(data, name, "java.concurrent.CountDownShouldInFinallyRule.violation.msg", + new Object[] {name.getImage()}); + } + } catch (JaxenException ignore) { + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadLocalShouldRemoveRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadLocalShouldRemoveRule.java new file mode 100644 index 0000000..e8a61b6 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadLocalShouldRemoveRule.java @@ -0,0 +1,89 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.List; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Customized ThreadLocal variables must be recycled, + * especially when using thread pools in which threads are often reused. + * Otherwise, it may affect subsequent business logic and cause unexpected problems such as memory leak. + * + * @author caikang + * @date 2017/03/29 + */ +public class ThreadLocalShouldRemoveRule extends AbstractAliRule { + private static final String XPATH_TPL = "//StatementExpression/PrimaryExpression" + + "/PrimaryPrefix/Name[@Image='%s.remove']"; + + private static final String METHOD_INITIAL_VALUE = "initialValue"; + + private static final String WITH_INITIAL = "ThreadLocal.withInitial"; + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + List fieldDeclarations = node.findDescendantsOfType(ASTFieldDeclaration.class); + if (fieldDeclarations == null || fieldDeclarations.isEmpty()) { + return super.visit(node, data); + } + for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { + if (fieldDeclaration.getType() == ThreadLocal.class) { + if (checkThreadLocalWithInitalValue(fieldDeclaration)) { continue; } + checkThreadLocal(fieldDeclaration, node, data); + } + } + return super.visit(node, data); + } + + private boolean checkThreadLocalWithInitalValue(ASTFieldDeclaration fieldDeclaration) { + ASTVariableDeclarator variableDeclarator = fieldDeclaration.getFirstDescendantOfType( + ASTVariableDeclarator.class); + if (variableDeclarator == null) { + return false; + } + ASTMethodDeclarator initialValueMethod = variableDeclarator.getFirstDescendantOfType(ASTMethodDeclarator.class); + if (initialValueMethod != null && METHOD_INITIAL_VALUE.equals(initialValueMethod.getImage())) { + return true; + } + ASTName name = variableDeclarator.getFirstDescendantOfType(ASTName.class); + return name != null && WITH_INITIAL.equals(name.getImage()); + } + + private void checkThreadLocal(ASTFieldDeclaration fieldDeclaration, ASTCompilationUnit node, Object data) { + try { + List nodes = node.findChildNodesWithXPath(String.format(XPATH_TPL, + fieldDeclaration.getVariableName())); + if (nodes == null || nodes.isEmpty()) { + ViolationUtils.addViolationWithPrecisePosition(this, fieldDeclaration, data, + I18nResources.getMessage("java.concurrent.ThreadLocalShouldRemoveRule.violation.msg", + fieldDeclaration.getVariableName())); + } + } catch (JaxenException ignore) { + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadPoolCreationRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadPoolCreationRule.java new file mode 100644 index 0000000..2a3f250 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadPoolCreationRule.java @@ -0,0 +1,115 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Executors; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.Token; + +/** + * [Mandatory] A thread pool should be created by ThreadPoolExecutor rather than Executors. + * These would make the parameters of the thread pool understandable. + * It would also reduce the risk of running out of system resource. + * + * @author caikang + * @date 2016/11/14 + */ +public class ThreadPoolCreationRule extends AbstractAliRule { + + private static final String DOT = "."; + private static final String COLON = ";"; + private static final String NEW = "new"; + private static final String EXECUTORS_NEW = Executors.class.getSimpleName() + DOT + NEW; + private static final String FULL_EXECUTORS_NEW = Executors.class.getName() + DOT + NEW; + private static final String BRACKETS = "()"; + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + Object superResult = super.visit(node, data); + + Info info = new Info(); + List importDeclarations = node.findChildrenOfType(ASTImportDeclaration.class); + for (ASTImportDeclaration importDeclaration : importDeclarations) { + ASTName name = importDeclaration.getFirstChildOfType(ASTName.class); + info.executorsUsed = info.executorsUsed + || (name.getType() == Executors.class || Executors.class.getName().equals(name.getImage())); + if (name.getImage().startsWith(Executors.class.getName() + DOT)) { + info.importedExecutorsMethods.add(name.getImage()); + } + } + List primaryExpressions = node.findDescendantsOfType(ASTPrimaryExpression.class); + for(ASTPrimaryExpression primaryExpression : primaryExpressions){ + if (!info.executorsUsed && info.importedExecutorsMethods.isEmpty()) { + continue; + } + + Token initToken = (Token)primaryExpression.jjtGetFirstToken(); + if (!checkInitStatement(initToken, info)) { + addViolationWithMessage(data, primaryExpression,"java.concurrent.ThreadPoolCreationRule.violation.msg"); + } + } + return superResult; + } + + private boolean checkInitStatement(Token token, Info info) { + String fullAssignStatement = getFullAssignStatement(token); + if (fullAssignStatement.startsWith(EXECUTORS_NEW)) { + return false; + } + if (!fullAssignStatement.startsWith(NEW) && !fullAssignStatement.startsWith(FULL_EXECUTORS_NEW)) { + return true; + } + // code with lambda + int index = fullAssignStatement.indexOf(BRACKETS); + if (index == -1) { + return true; + } + fullAssignStatement = fullAssignStatement.substring(0, index); + // java.util.concurrent.Executors.newxxxx + if (info.importedExecutorsMethods.contains(fullAssignStatement)) { + return false; + } + // static import + return !info.importedExecutorsMethods.contains(Executors.class.getName() + DOT + fullAssignStatement); + } + + private String getFullAssignStatement(final Token token) { + if (token == null) { + return ""; + } + StringBuilder sb = new StringBuilder(48); + Token next = token; + while (next.next != null && !COLON.equals(next.image)) { + sb.append(next.image); + next = next.next; + } + return sb.toString(); + } + + class Info { + boolean executorsUsed; + Set importedExecutorsMethods = new HashSet<>(); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadShouldSetNameRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadShouldSetNameRule.java new file mode 100644 index 0000000..4ed626a --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ThreadShouldSetNameRule.java @@ -0,0 +1,118 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; + +/** + * [Mandatory] A meaningful thread name is helpful to trace the error information, + * so assign a name when creating threads or thread pools. + * + * Detection rule //TODO should review + * 1. Use specific constructor while create thread pool + * 2. Use Executors.defaultThreadFactory() is not allowed + * + * @author caikang + * @date 2016/11/16 + * @see AvoidManuallyCreateThreadRule + */ +public class ThreadShouldSetNameRule extends AbstractAliRule { + private static final int ARGUMENT_LENGTH_2 = 2; + private static final int ARGUMENT_LENGTH_6 = 6; + private static final int INDEX_1 = 1; + private static final int SINGLE_LENGTH = 1; + + private static final String MESSAGE_KEY_PREFIX = "java.concurrent.ThreadShouldSetNameRule.violation.msg"; + + @Override + public Object visit(ASTAllocationExpression node, Object data) { + //Custom Class + if (node.getType() == null) { + return super.visit(node, data); + } + if (!ExecutorService.class.isAssignableFrom(node.getType())) { + return super.visit(node, data); + } + if (ThreadPoolExecutor.class == node.getType()) { + return checkThreadPoolExecutor(node, data); + } + if (ScheduledThreadPoolExecutor.class == node.getType()) { + return checkSchedulePoolExecutor(node, data); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + return super.visit(node, data); + } + + private Object checkThreadPoolExecutor(ASTAllocationExpression node, Object data) { + ASTArgumentList argumentList = node.getFirstDescendantOfType(ASTArgumentList.class); + if (argumentList.jjtGetNumChildren() < ARGUMENT_LENGTH_6 + || !checkThreadFactoryArgument((ASTExpression)argumentList.jjtGetChild(ARGUMENT_LENGTH_6 - INDEX_1))) { + addViolationWithMessage(data, node, MESSAGE_KEY_PREFIX + ".ThreadPoolExecutor"); + } + return super.visit(node, data); + } + + private Object checkSchedulePoolExecutor(ASTAllocationExpression node, Object data) { + ASTArgumentList argumentList = node.getFirstDescendantOfType(ASTArgumentList.class); + if (argumentList.jjtGetNumChildren() < ARGUMENT_LENGTH_2 + || !checkThreadFactoryArgument((ASTExpression)argumentList.jjtGetChild(ARGUMENT_LENGTH_2 - INDEX_1))) { + addViolationWithMessage(data, node, MESSAGE_KEY_PREFIX + ".ScheduledThreadPoolExecutor"); + } + return super.visit(node, data); + } + + private boolean checkThreadFactoryArgument(ASTExpression expression) { + if (expression.getType() != null && ThreadFactory.class.isAssignableFrom(expression.getType())) { + return true; + } + ASTName name = expression.getFirstDescendantOfType(ASTName.class); + if (name != null && name.getType() == Executors.class) { + return false; + } + ASTLambdaExpression lambdaExpression = expression.getFirstDescendantOfType(ASTLambdaExpression.class); + if (lambdaExpression != null) { + List variableDeclaratorIds = + lambdaExpression.findChildrenOfType(ASTVariableDeclaratorId.class); + if (variableDeclaratorIds == null || variableDeclaratorIds.size() != SINGLE_LENGTH) { + return false; + } + } else if (expression.getType() != null + && RejectedExecutionHandler.class.isAssignableFrom(expression.getType())) { + return false; + } + return true; + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UndefineMagicConstantRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UndefineMagicConstantRule.java new file mode 100644 index 0000000..e253188 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UndefineMagicConstantRule.java @@ -0,0 +1,118 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.constant; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Magic values, except for predefined, are forbidden in coding. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class UndefineMagicConstantRule extends AbstractAliRule { + + /** + * white list for undefined variable, may be added + */ + private final static List LITERAL_WHITE_LIST = NameListConfig.NAME_LIST_SERVICE.getNameList( + UndefineMagicConstantRule.class.getSimpleName(), "LITERAL_WHITE_LIST"); + + private final static String XPATH = "//Literal/../../../../..[not(VariableInitializer)]"; + + /** + * An undefined that belongs to non-looped if statements + * + * @param node compilation unit + * @param data rule context + */ + @Override + public Object visit(ASTCompilationUnit node, Object data) { + // removed repeat magic value , to prevent the parent class to find sub-variable nodes when there is a repeat + List currentLiterals = new ArrayList(); + try { + // Find the parent node of the undefined variable + List parentNodes = node.findChildNodesWithXPath(XPATH); + + for (Node parentItem : parentNodes) { + List literals = parentItem.findDescendantsOfType(ASTLiteral.class); + for (ASTLiteral literal : literals) { + if (inBlackList(literal) && !currentLiterals.contains(literal)) { + currentLiterals.add(literal); + addViolationWithMessage(data, literal, + "java.constant.UndefineMagicConstantRule.violation.msg", + new Object[] {literal.getImage()}); + } + } + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } + + /** + * Undefined variables are in the blacklist + * + * @param literal + * @return + */ + private boolean inBlackList(ASTLiteral literal) { + String name = literal.getImage(); + int lineNum = literal.getBeginLine(); + // name is null,bool literal,belongs to white list + if (name == null) { + return false; + } + // filter white list + for (String whiteItem : LITERAL_WHITE_LIST) { + if (whiteItem.equals(name)) { + return false; + } + } + ASTIfStatement ifStatement = literal.getFirstParentOfType(ASTIfStatement.class); + if (ifStatement != null && lineNum == ifStatement.getBeginLine()) { + ASTForStatement forStatement = ifStatement.getFirstParentOfType(ASTForStatement.class); + ASTWhileStatement whileStatement = ifStatement.getFirstParentOfType(ASTWhileStatement.class); + if (forStatement != null || whileStatement != null) { + return false; + } + return true; + } + + // judge magic value belongs to for statement + ASTForStatement blackForStatement = literal.getFirstParentOfType(ASTForStatement.class); + if (blackForStatement != null && lineNum == blackForStatement.getBeginLine()) { + return true; + } + + // judge magic value belongs to while statement + ASTWhileStatement blackWhileStatement = literal.getFirstParentOfType(ASTWhileStatement.class); + return blackWhileStatement != null && lineNum == blackWhileStatement.getBeginLine(); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UpperEllRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UpperEllRule.java new file mode 100644 index 0000000..a5178bb --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/constant/UpperEllRule.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.constant; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; + +/** + * [Mandatory] 'L' instead of 'l' should be used for long or Long variable because 'l' is easily to + * be regarded as number 1 in mistake. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class UpperEllRule extends AbstractAliRule { + private static final String LOWERCASE_L = "l"; + + @Override + public Object visit(ASTLiteral node, Object data) { + String image = node.getImage(); + // if it is an integer and ends with l, collects the current violation code + if (image != null && node.isLongLiteral() && image.endsWith(LOWERCASE_L)) { + addViolationWithMessage(data, node, "java.constant.UpperEllRule.violation.msg", + new Object[] {node.getImage()}); + } + return super.visit(node, data); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/AvoidReturnInFinallyRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/AvoidReturnInFinallyRule.java new file mode 100644 index 0000000..5ade344 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/AvoidReturnInFinallyRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.exception; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] Never use return within a finally block. + * A return statement in a finally block will cause exception or result + * in a discarded return value in the try-catch block. + * + * @author zenghou.fw + * @date 2017/03/29 + */ +public class AvoidReturnInFinallyRule extends AbstractXpathRule { + private static final String XPATH = "//FinallyStatement//ReturnStatement"; + + public AvoidReturnInFinallyRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.exception.AvoidReturnInFinallyRule.violation.msg")); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/MethodReturnWrapperTypeRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/MethodReturnWrapperTypeRule.java new file mode 100644 index 0000000..e84c1a4 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/MethodReturnWrapperTypeRule.java @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.exception; + +import java.util.List; +import java.util.Map; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import org.jaxen.JaxenException; + +/** + * [Recommended] If the return type is primitive, return a value of wrapper class may cause NullPointerException. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class MethodReturnWrapperTypeRule extends AbstractAliRule { + private static final Map PRIMITIVE_TYPE_TO_WAPPER_TYPE = NameListConfig.NAME_LIST_SERVICE + .getNameMap("MethodReturnWrapperTypeRule", "PRIMITIVE_TYPE_TO_WAPPER_TYPE", String.class, String.class); + private static final String METHOD_RETURN_TYPE_XPATH = "ResultType/Type/PrimitiveType"; + private static final String METHOD_RETURN_OBJECT_XPATH + = "Block/BlockStatement/Statement/ReturnStatement/Expression/PrimaryExpression/PrimaryPrefix/Name"; + private static final String METHOD_VARIABLE_TYPE_XPATH = "Type/ReferenceType/ClassOrInterfaceType"; + private static final String METHOD_VARIABLE_NAME_XPATH + = "Block/BlockStatement/LocalVariableDeclaration/VariableDeclarator/VariableDeclaratorId"; + + @Override + public Object visit(ASTMethodDeclaration node, Object data) { + try { + List astPrimitiveTypeList = node.findChildNodesWithXPath(METHOD_RETURN_TYPE_XPATH); + //check the method return type + if (!(astPrimitiveTypeList != null && astPrimitiveTypeList.size() == 1)) { + return super.visit(node, data); + } + ASTPrimitiveType astPrimitiveType = (ASTPrimitiveType)astPrimitiveTypeList.get(0); + //If the return type is not a basic types,skip + if (!(astPrimitiveType.getType() != null && astPrimitiveType.getType().isPrimitive())) { + return super.visit(node, data); + } + //the return type + String primitiveTypeName = astPrimitiveType.getType().getName(); + //the return node + List nameList = node.findChildNodesWithXPath(METHOD_RETURN_OBJECT_XPATH); + if (nameList == null || nameList.size() != 1) { + return super.visit(node, data); + } + //if the local variable is empty,skip + List methodVariableNameList = node.findChildNodesWithXPath(METHOD_VARIABLE_NAME_XPATH); + if (methodVariableNameList == null || methodVariableNameList.size() == 0) { + return super.visit(node, data); + } + ASTName astName = (ASTName)nameList.get(0); + String variableName = astName.getImage(); + //iterate all the method of variable nodes + for (Node methodVariableNameNode : methodVariableNameList) { + ASTVariableDeclaratorId astVariableDeclaratorId + = (ASTVariableDeclaratorId)methodVariableNameNode; + //find out the variable named the same with return node + if (!variableName.equals(astVariableDeclaratorId.getImage())) { + continue; + } + ASTLocalVariableDeclaration astLocalVariableDeclaration = astVariableDeclaratorId + .getFirstParentOfType(ASTLocalVariableDeclaration.class); + //check local variables type + List nodeList = astLocalVariableDeclaration.findChildNodesWithXPath( + METHOD_VARIABLE_TYPE_XPATH); + + if (nodeList != null && nodeList.size() == 1) { + ASTClassOrInterfaceType astClassOrInterfaceType = (ASTClassOrInterfaceType)nodeList.get( + 0); + //if variable type is a value of wrapper + if (PRIMITIVE_TYPE_TO_WAPPER_TYPE.get(primitiveTypeName) != null + && PRIMITIVE_TYPE_TO_WAPPER_TYPE.get(primitiveTypeName).equals( + astClassOrInterfaceType.getType().getSimpleName())) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.exception.MethodReturnWrapperTypeRule.violation.msg", + primitiveTypeName, astClassOrInterfaceType.getType().getSimpleName())); + } + } + } + } catch (JaxenException e) { + return super.visit(node, data); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/TransactionMustHaveRollbackRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/TransactionMustHaveRollbackRule.java new file mode 100644 index 0000000..98cc89a --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/exception/TransactionMustHaveRollbackRule.java @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.exception; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Make sure to invoke the rollback if a method throws an Exception. + * + * @author caikang + * @date 2017/03/29 + */ +public class TransactionMustHaveRollbackRule extends AbstractAliRule { + private static final String TRANSACTIONAL_ANNOTATION_NAME = "Transactional"; + private static final String TRANSACTIONAL_FULL_NAME = "org.springframework.transaction.annotation." + + TRANSACTIONAL_ANNOTATION_NAME; + private static final String ROLLBACK_FOR = "rollbackFor"; + + private static final String XPATH_FOR_ROLLBACK = "//StatementExpression/PrimaryExpression" + + "/PrimaryPrefix/Name[ends-with(@Image,'rollback')]"; + + private static final String MESSAGE_KEY_PREFIX = "java.exception.TransactionMustHaveRollbackRule.violation.msg"; + + @Override + public Object visit(ASTAnnotation node, Object data) { + ASTName name = node.getFirstDescendantOfType(ASTName.class); + boolean noTransactional = name == null || !(TRANSACTIONAL_ANNOTATION_NAME.equals(name.getImage()) + && !TRANSACTIONAL_FULL_NAME.equals(name.getImage())); + if (noTransactional) { + return super.visit(node, data); + } + List memberValuePairList = node.findDescendantsOfType(ASTMemberValuePair.class); + if (rollbackAttrSet(memberValuePairList)) { + return super.visit(node, data); + } + + ASTClassOrInterfaceDeclaration classOrInterfaceDeclaration + = getSiblingForType(node, ASTClassOrInterfaceDeclaration.class); + if (classOrInterfaceDeclaration != null) { + addViolationWithMessage(data, node, MESSAGE_KEY_PREFIX + ".simple"); + return super.visit(node, data); + } + + ASTMethodDeclaration methodDeclaration = getSiblingForType(node, ASTMethodDeclaration.class); + if (methodDeclaration == null) { + return super.visit(node, data); + } + try { + List nodes = methodDeclaration.findChildNodesWithXPath(XPATH_FOR_ROLLBACK); + if (nodes != null && !nodes.isEmpty()) { + return super.visit(node, data); + } + addViolationWithMessage(data, methodDeclaration, MESSAGE_KEY_PREFIX, + new Object[] {methodDeclaration.getMethodName()}); + } catch (JaxenException ignore) { + } + return super.visit(node, data); + } + + private boolean rollbackAttrSet(List memberValuePairList) { + for (ASTMemberValuePair pair : memberValuePairList) { + if (ROLLBACK_FOR.equals(pair.getImage())) { + return true; + } + } + return false; + } + + /** + * annotation is sibling of classOrInterface declaration or method declaration + * + * @param node transactional annotation + * @param clz classOrInterface declaration or method declaration + * @param generic + * @return sibling node + */ + private T getSiblingForType(ASTAnnotation node, Class clz) { + Node parent = node.jjtGetParent(); + int num = parent.jjtGetNumChildren(); + for (int i = 0; i < num; i++) { + Node child = parent.jjtGetChild(i); + if (clz.isAssignableFrom(child.getClass())) { + return clz.cast(child); + } + if (!(child instanceof ASTAnnotation)) { + return null; + } + } + return null; + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/AvoidComplexConditionRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/AvoidComplexConditionRule.java new file mode 100644 index 0000000..e97b649 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/AvoidComplexConditionRule.java @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.flowcontrol; + +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] Do not use complicated statements in conditional statements (except for frequently used methods + * like getXxx/isXxx). Use boolean variables to store results of complicated statements temporarily will increase + * the code's readability. + * + * @author zenghou.fw + * @date 2017/04/11 + */ +public class AvoidComplexConditionRule extends AbstractXpathRule { + private static final String XPATH = "(//IfStatement/Expression" + + "|//ConditionalExpression[@Ternary = 'true']/PrimaryExpression)" + + "[count(.//ConditionalAndExpression) + count(.//ConditionalOrExpression) > 1]"; + + public AvoidComplexConditionRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + "java.flowcontrol.AvoidComplexConditionRule.violation.msg"); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/NeedBraceRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/NeedBraceRule.java new file mode 100644 index 0000000..a73a520 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/NeedBraceRule.java @@ -0,0 +1,75 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.flowcontrol; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; +import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; + +/** + * [Mandatory] Braces are used with if, else, for, do and while statements, even if the body contains only + * a single statement. Avoid using the following example: + *
+ * if (condition) statements;
+ * 
+ * + * @author zenghou.fw + * @date 2016/11/22 + */ +public class NeedBraceRule extends AbstractAliRule { + private static final String STATEMENT_BLOCK = "Statement/Block"; + + private static final String MESSAGE_KEY = "java.flowcontrol.NeedBraceRule.violation.msg"; + + @Override + public Object visit(ASTIfStatement node, Object data) { + // SwitchStatement without {} fail by compilaton, no need to check here + if (!node.hasDescendantMatchingXPath(STATEMENT_BLOCK)) { + addViolationWithMessage(data, node, MESSAGE_KEY, + new Object[] {node.jjtGetFirstToken().toString()}); + } + if (node.hasElse()) { + // IfStatement with else have 2 expression blocks, should never throws NPE + ASTStatement elseStms = node.findChildrenOfType(ASTStatement.class).get(1); + + if (!elseStms.hasDecendantOfAnyType(ASTBlock.class, ASTIfStatement.class)) { + addViolationWithMessage(data, elseStms, MESSAGE_KEY, new Object[] {"else"}); + } + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTForStatement node, Object data) { + if (!node.hasDescendantMatchingXPath(STATEMENT_BLOCK)) { + addViolationWithMessage(data, node, MESSAGE_KEY, new Object[] {"for"}); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTWhileStatement node, Object data) { + if (!node.hasDescendantMatchingXPath(STATEMENT_BLOCK)) { + addViolationWithMessage(data, node, MESSAGE_KEY, new Object[] {"while"}); + } + return super.visit(node, data); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/SwitchStatementRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/SwitchStatementRule.java new file mode 100644 index 0000000..514db23 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/SwitchStatementRule.java @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.flowcontrol; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement; + +/** + * [Mandatory] In a switch block, each case should be finished by break/return. + * If not, a note should be included to describe at which case it will stop. Within every switch block, + * a default statement must be present, even if it is empty. + * + * @author zenghou.fw + * @date 2016/11/17 + */ +public class SwitchStatementRule extends AbstractAliRule { + private static final String MESSAGE_KEY_PREFIX = "java.flowcontrol.SwitchStatementRule.violation"; + + @Override + public Object visit(ASTSwitchStatement node, Object data) { + checkDefault(node, data); + + checkFallThrough(node, data); + + return super.visit(node, data); + } + + /** + * Check if switch statement contains default branch + * + * @param node + * @param data + */ + private void checkDefault(ASTSwitchStatement node, Object data) { + final String switchCheckXpath = "SwitchLabel[@Default='true']"; + if (!node.hasDescendantMatchingXPath(switchCheckXpath)) { + addViolationWithMessage(data, node, MESSAGE_KEY_PREFIX + ".nodefault"); + } + } + + /** + * Check the availability of break, return, throw, continue in case statement + * + * @param node + * @param data + */ + private void checkFallThrough(ASTSwitchStatement node, Object data) { + // refer the rule MissingBreakInSwitch of PMD + final String xpath = "../SwitchStatement[(count(.//BreakStatement)" + + " + count(BlockStatement//Statement/ReturnStatement)" + + " + count(BlockStatement//Statement/ThrowStatement)" + + " + count(BlockStatement//Statement/IfStatement[@Else='true' and " + + "Statement[2][ReturnStatement|ThrowStatement]]" + + + "/Statement[1][ReturnStatement|ThrowStatement])" + + " + count(SwitchLabel[name(following-sibling::node()) = 'SwitchLabel'])" + + " + count(SwitchLabel[count(following-sibling::node()) = 0])" + + " < count (SwitchLabel[@Default != 'true']))]"; + + if (node.hasDescendantMatchingXPath(xpath)) { + addViolationWithMessage(data, node, MESSAGE_KEY_PREFIX + ".notermination"); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AbstractClassShouldStartWithAbstractNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AbstractClassShouldStartWithAbstractNamingRule.java new file mode 100644 index 0000000..0e0509e --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AbstractClassShouldStartWithAbstractNamingRule.java @@ -0,0 +1,49 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; + +/** + * [Mandatory] Abstract class names must start with Abstract or Base. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class AbstractClassShouldStartWithAbstractNamingRule extends AbstractXpathRule { + private static final String XPATH = "//ClassOrInterfaceDeclaration\n" + + " [@Abstract='true' and @Interface='false']\n" + " [not (matches(@Image,'^(Abstract|Base).*'))]"; + + public AbstractClassShouldStartWithAbstractNamingRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + if (node instanceof ASTClassOrInterfaceDeclaration) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.AbstractClassShouldStartWithAbstractNamingRule.violation.msg", + node.getImage())); + } else { + super.addViolation(data, node, arg); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ArrayNamingShouldHaveBracketRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ArrayNamingShouldHaveBracketRule.java new file mode 100644 index 0000000..ef09a9c --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ArrayNamingShouldHaveBracketRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] Brackets are a part of an Array type. The definition could be: String[] args; + * + * @author changle.lq + * @date 2017/04/16 + */ +public class ArrayNamingShouldHaveBracketRule extends AbstractXpathRule { + private static final String XPATH = "//VariableDeclaratorId\n" + "[../..[@Array = 'true']]\n" + + "[../../Type/ReferenceType[@Array != 'true']]"; + + public ArrayNamingShouldHaveBracketRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.ArrayNamingShouldHaveBracketRule.violation.msg", + node.getImage())); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AvoidStartWithDollarAndUnderLineNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AvoidStartWithDollarAndUnderLineNamingRule.java new file mode 100644 index 0000000..562d954 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/AvoidStartWithDollarAndUnderLineNamingRule.java @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; + +/** + * [Mandatory] All names should not start or end with an underline or a dollar sign. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class AvoidStartWithDollarAndUnderLineNamingRule extends AbstractAliRule { + private static final String DOLLAR = "$"; + private static final String UNDERSCORE = "_"; + private static final String FORMAT = I18nResources.getMessage( + "java.naming.AvoidStartWithDollarAndUnderLineNamingRule.violation.msg"); + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.getImage().startsWith(DOLLAR) || node.getImage().startsWith(UNDERSCORE)) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, String.format(FORMAT, node.getImage())); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTVariableDeclaratorId node, Object data) { + if (node.getImage().startsWith(DOLLAR) || node.getImage().startsWith(UNDERSCORE)) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, String.format(FORMAT, node.getImage())); + } + return super.visit(node, data); + } + + @Override + public Object visit(ASTMethodDeclarator node, Object data) { + if (node.getImage().startsWith(DOLLAR) || node.getImage().startsWith(UNDERSCORE)) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, String.format(FORMAT, node.getImage())); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/BooleanPropertyShouldNotStartWithIsRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/BooleanPropertyShouldNotStartWithIsRule.java new file mode 100644 index 0000000..1e0c96b --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/BooleanPropertyShouldNotStartWithIsRule.java @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; + +/** + * [Mandatory] Do not add 'is' as prefix while defining Boolean variable, since it may cause a serialization exception + * in some Java Frameworks. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class BooleanPropertyShouldNotStartWithIsRule extends AbstractXpathRule { + private static final String XPATH = "//VariableDeclaratorId\n" + "[(ancestor::ClassOrInterfaceDeclaration)[\n" + + "@Interface='false'\n" + "and\n" + "( ends-with(@Image, 'DO')\n" + "or ends-with(@Image, 'DTO')\n" + + "or ends-with(@Image, 'VO')\n" + "or ends-with(@Image, 'DAO')\n" + ")\n" + "]]\n" + + "[../../../FieldDeclaration/Type/PrimitiveType[@Image = 'boolean']]\n" + "[.[ starts-with(@Image, 'is')]]"; + + public BooleanPropertyShouldNotStartWithIsRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + if (node instanceof ASTVariableDeclaratorId) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.BooleanPropertyShouldNotStartWithIsRule.violation.msg", + node.getImage())); + } else { + super.addViolation(data, node, arg); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ClassNamingShouldBeCamelRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ClassNamingShouldBeCamelRule.java new file mode 100644 index 0000000..57ead3d --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ClassNamingShouldBeCamelRule.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import java.util.List; +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; + +/** + * [Mandatory] Class names should be nouns in UpperCamelCase except domain models: DO, BO, DTO, VO, etc. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class ClassNamingShouldBeCamelRule extends AbstractAliRule { + + private static final Pattern PATTERN + = Pattern.compile("^I?([A-Z][a-z0-9]+)+(([A-Z])|(DO|DTO|VO|DAO|BO|DAOImpl|YunOS|AO|PO))?$"); + + private static final List CLASS_NAMING_WHITE_LIST = NameListConfig.NAME_LIST_SERVICE.getNameList( + ClassNamingShouldBeCamelRule.class.getSimpleName(), "CLASS_NAMING_WHITE_LIST"); + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + for (String s : CLASS_NAMING_WHITE_LIST) { + if (node.getImage().contains(s)) { + return super.visit(node, data); + } + } + if (PATTERN.matcher(node.getImage()).matches()) { + return super.visit(node, data); + } + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.ClassNamingShouldBeCamelRule.violation.msg", + node.getImage())); + + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ConstantFieldShouldBeUpperCaseRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ConstantFieldShouldBeUpperCaseRule.java new file mode 100644 index 0000000..1b7585b --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ConstantFieldShouldBeUpperCaseRule.java @@ -0,0 +1,69 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import java.util.HashSet; +import java.util.Set; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import org.apache.commons.lang3.StringUtils; + +/** + * [Mandatory] Constant variable names should be written in upper characters separated by underscores. These names + * should be semantically complete and clear. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class ConstantFieldShouldBeUpperCaseRule extends AbstractAliRule { + private static final String SERVICE_SUFFIX = "Service"; + private static final Set LOG_VARIABLE_TYPE_SET = new HashSet<>(NameListConfig.NAME_LIST_SERVICE.getNameList( + "ConstantFieldShouldBeUpperCaseRule", "LOG_VARIABLE_TYPE_SET")); + private static final Set WHITE_LIST = new HashSet<>(NameListConfig.NAME_LIST_SERVICE.getNameList( + "ConstantFieldShouldBeUpperCaseRule", "WHITE_LIST")); + + @Override + public Object visit(ASTFieldDeclaration node, Object data) { + if (!(node.isStatic() && node.isFinal())) { + return super.visit(node, data); + } + //If the variable is of type Log or Logger,do not check + ASTClassOrInterfaceType classOrInterfaceType = node.getFirstDescendantOfType(ASTClassOrInterfaceType.class); + if (classOrInterfaceType != null && LOG_VARIABLE_TYPE_SET.contains(classOrInterfaceType.getImage())) { + return super.visit(node, data); + } + //filter by white list,such as the serialVersionUID + String constantName = node.jjtGetChild(1).jjtGetChild(0).getImage(); + boolean inWhiteList = StringUtils.isEmpty(constantName) || WHITE_LIST.contains(constantName) + || constantName.endsWith(SERVICE_SUFFIX); + if (inWhiteList) { + return super.visit(node, data); + } + //Constant should be upper + if (!(constantName.equals(constantName.toUpperCase()))) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.ConstantFieldShouldBeUpperCaseRule.violation.msg", + constantName)); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ExceptionClassShouldEndWithExceptionRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ExceptionClassShouldEndWithExceptionRule.java new file mode 100644 index 0000000..2e5cb3d --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ExceptionClassShouldEndWithExceptionRule.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTExtendsList; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; +import org.apache.commons.lang3.StringUtils; + +/** + * [Mandatory] Exception class names must be ended with Exception. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class ExceptionClassShouldEndWithExceptionRule extends AbstractAliRule { + + private static final String EXCEPTION_END_SUFFIX = "Exception"; + + @Override + public Object visit(ASTExtendsList node, Object data) { + ASTClassOrInterfaceType astClassOrInterfaceType = node.getFirstChildOfType(ASTClassOrInterfaceType.class); + if ((astClassOrInterfaceType == null) || (!(TypeHelper.isA(astClassOrInterfaceType, Throwable.class)))) { + return super.visit(node, data); + } + + ASTClassOrInterfaceDeclaration astClassOrInterfaceDeclaration = node.getFirstParentOfType( + ASTClassOrInterfaceDeclaration.class); + boolean isExceptionViolation = astClassOrInterfaceDeclaration != null + && StringUtils.isNotEmpty(astClassOrInterfaceDeclaration.getImage()) + && !astClassOrInterfaceDeclaration.getImage().endsWith(EXCEPTION_END_SUFFIX); + if (isExceptionViolation) { + ViolationUtils.addViolationWithPrecisePosition(this, astClassOrInterfaceDeclaration, data, + I18nResources.getMessage("java.naming.ExceptionClassShouldEndWithExceptionRule.violation.msg", + astClassOrInterfaceDeclaration.getImage())); + } + return super.visit(node, data); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java new file mode 100644 index 0000000..087d7a4 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/LowerCamelCaseVariableNamingRule.java @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; + +/** + * [Mandatory] Method names, parameter names, member variable names, and local variable names should be written in + * lowerCamelCase. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class LowerCamelCaseVariableNamingRule extends AbstractAliRule { + + private static final String MESSAGE_KEY_PREFIX = "java.naming.LowerCamelCaseVariableNamingRule.violation.msg"; + private Pattern pattern = Pattern.compile("^[a-z|$][a-z0-9]*([A-Z][a-z0-9]*)*(DO|DTO|VO|DAO)?$"); + + @Override + public Object visit(final ASTVariableDeclaratorId node, Object data) { + // Constant named does not apply to this rule + ASTFieldDeclaration astFieldDeclaration = node.getFirstParentOfType(ASTFieldDeclaration.class); + boolean isNotCheck = astFieldDeclaration != null && (astFieldDeclaration.isFinal() || astFieldDeclaration + .isStatic()); + if (isNotCheck) { + return super.visit(node, data); + } + + // variable naming violate lowerCamelCase + if (!(pattern.matcher(node.getImage()).matches())) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".variable", node.getImage())); + } + return super.visit(node, data); + } + + @Override + + public Object visit(ASTMethodDeclarator node, Object data) { + if (!(pattern.matcher(node.getImage()).matches())) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".method", node.getImage())); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/PackageNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/PackageNamingRule.java new file mode 100644 index 0000000..fb3ff15 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/PackageNamingRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] Package should be named in lowercase characters. There should be only one English word after + * each dot. Package names are always in singular format while class name can be in plural format if necessary. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class PackageNamingRule extends AbstractXpathRule { + private static final String XPATH = "//PackageDeclaration/Name\n" + + "[not (matches(@Image, '^[a-z]+(\\.[a-z][a-z0-9]*)*$'))]"; + + public PackageNamingRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.PackageNamingRule.violation.msg", node.getImage())); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ServiceOrDaoClassShouldEndWithImplRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ServiceOrDaoClassShouldEndWithImplRule.java new file mode 100644 index 0000000..b9a0b28 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/ServiceOrDaoClassShouldEndWithImplRule.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] All Service and DAO classes must be interface based on SOA principle. Implementation class names + * should be ended with Impl. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class ServiceOrDaoClassShouldEndWithImplRule extends AbstractXpathRule { + private static final String XPATH = "//ClassOrInterfaceDeclaration\n" + + "[ .[@Interface='false'] and .[@Abstract='false'] and ./ImplementsList/ClassOrInterfaceType[ ends-with(@Image, 'Service') or " + + "ends-with(@Image, 'DAO')]]\n" + + "[not(.[ ends-with(@Image, 'Impl')])]"; + + public ServiceOrDaoClassShouldEndWithImplRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.ServiceOrDaoClassShouldEndWithImplRule.violation.msg", + node.getImage())); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/TestClassShouldEndWithTestNamingRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/TestClassShouldEndWithTestNamingRule.java new file mode 100644 index 0000000..61ae516 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/naming/TestClassShouldEndWithTestNamingRule.java @@ -0,0 +1,88 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import java.util.List; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.rule.junit.AbstractJUnitRule; + +/** + * [Mandatory] Test cases shall be started with the class names to be tested and ended with Test. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class TestClassShouldEndWithTestNamingRule extends AbstractJUnitRule { + private static final String TEST_SUFFIX = "Test"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isAbstract() || node.isInterface() || node.isNested()) { + return super.visit(node, data); + } + + List m = node.findDescendantsOfType(ASTMethodDeclaration.class); + boolean testsFound = false; + + if (m != null) { + for (ASTMethodDeclaration md : m) { + if (!isInInnerClassOrInterface(md) && isJUnitMethod(md, data)) { + testsFound = true; + } + } + } + + if ((testsFound) && (!(node.getImage().endsWith(TEST_SUFFIX)))) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.TestClassShouldEndWithTestNamingRule.violation.msg", + node.getImage())); + } + + return super.visit(node, data); + } + + private boolean isInInnerClassOrInterface(ASTMethodDeclaration md) { + ASTClassOrInterfaceDeclaration p = md.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); + return p != null && p.isNested(); + } + + @Override + public void setDescription(String description) { + super.setDescription(I18nResources.getMessageWithExceptionHandled(description)); + } + + @Override + public void setMessage(String message) { + super.setMessage(I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message) { + super.addViolationWithMessage(data, node, I18nResources.getMessageWithExceptionHandled(message)); + } + + @Override + public void addViolationWithMessage(Object data, Node node, String message, Object[] args) { + super.addViolationWithMessage(data, node, + String.format(I18nResources.getMessageWithExceptionHandled(message), args)); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/EqualsAvoidNullRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/EqualsAvoidNullRule.java new file mode 100644 index 0000000..5de8019 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/EqualsAvoidNullRule.java @@ -0,0 +1,123 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils; +import com.alibaba.p3c.pmd.lang.java.util.StringAndCharConstants; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.java.ast.Token; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Since NullPointerException can possibly be thrown while calling the equals method of Object, + * equals should be invoked by a constant or an object that is definitely not null. + * + * @author zenghou.fw + * @date 2016/11/29 + */ +public class EqualsAvoidNullRule extends AbstractAliRule { + + private static final String XPATH = "//PrimaryExpression[" + "(PrimaryPrefix[Name[(ends-with(@Image, '.equals'))]]|" + + "PrimarySuffix[@Image='equals'])" + + "[(../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix) and " + + "( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 )]]" + + "[not(ancestor::Expression/ConditionalAndExpression//EqualityExpression[@Image='!=']//NullLiteral)]" + + "[not(ancestor::Expression/ConditionalOrExpression//EqualityExpression[@Image='==']//NullLiteral)]"; + + private static final String INVOCATION_PREFIX_XPATH + = "PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression[not(PrimarySuffix)]/PrimaryPrefix"; + + private static final String METHOD_EQUALS = "equals"; + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + try { + List equalsInvocations = node.findChildNodesWithXPath(XPATH); + if (equalsInvocations == null || equalsInvocations.isEmpty()) { + return super.visit(node, data); + } + for (Node invocation : equalsInvocations) { + // if arguments of equals is complicate expression, skip the check + List simpleExpressions = invocation.findChildNodesWithXPath(INVOCATION_PREFIX_XPATH); + if (simpleExpressions == null || simpleExpressions.isEmpty()) { + return super.visit(node, data); + } + + ASTPrimaryPrefix right = (ASTPrimaryPrefix)simpleExpressions.get(0); + if (right.getFirstChildOfType(ASTLiteral.class) != null) { + ASTLiteral literal = right.getFirstChildOfType(ASTLiteral.class); + if (literal.isStringLiteral()) { + // other literals has no equals method, can not be flipped + addRuleViolation(data, invocation); + } + } else { + ASTName name = right.getFirstChildOfType(ASTName.class); + // TODO works only in current compilation file, by crossing files will be null + boolean nameInvalid = name == null || name.getNameDeclaration() == null + || name.getNameDeclaration().getNode() == null; + if (nameInvalid) { + return super.visit(node, data); + } + Node nameNode = name.getNameDeclaration().getNode(); + if ((nameNode instanceof ASTVariableDeclaratorId) && (nameNode.getNthParent( + 2) instanceof ASTFieldDeclaration)) { + ASTFieldDeclaration field = (ASTFieldDeclaration)nameNode.getNthParent(2); + if (NodeUtils.isConstant(field)) { + addRuleViolation(data, invocation); + } + } + } + } + } catch (JaxenException e) { + throw new RuntimeException("XPath expression " + XPATH + " failed: " + e.getLocalizedMessage(), e); + } + return super.visit(node, data); + } + + private String getInvocationName(AbstractJavaNode javaNode) { + Token token = (Token)javaNode.jjtGetFirstToken(); + StringBuilder sb = new StringBuilder(token.image).append(token.image); + while (token.next != null && token.next.image != null && !METHOD_EQUALS.equals(token.next.image)) { + token = token.next; + sb.append(token.image); + } + if (sb.charAt(sb.length() - 1) == StringAndCharConstants.DOT) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } + + private void addRuleViolation(Object data, Node invocation) { + if (invocation instanceof AbstractJavaNode) { + AbstractJavaNode javaNode = (AbstractJavaNode)invocation; + addViolationWithMessage(data, invocation, "java.oop.EqualsAvoidNullRule.violation.msg", + new Object[] {getInvocationName(javaNode)}); + } else { + addViolation(data, invocation); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustOverrideToStringRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustOverrideToStringRule.java new file mode 100644 index 0000000..b14e545 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustOverrideToStringRule.java @@ -0,0 +1,107 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractPojoRule; +import com.alibaba.p3c.pmd.lang.java.util.PojoUtils; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; +import net.sourceforge.pmd.lang.java.ast.ASTExtendsList; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import org.jaxen.JaxenException; + +/** + * [Mandatory] The toString method must be implemented in a POJO class. The super.toString method should be called + * in front of the whole implementation if the current class extends another POJO class. + * + * @author zenghou.fw + * @date 2016/11/25 + */ +public class PojoMustOverrideToStringRule extends AbstractPojoRule { + + private static final String XPATH = "ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration" + + "[@Public='true' and MethodDeclarator[@Image='toString'] and " + + "MethodDeclarator[@Image='toString' and @ParameterCount='0']]"; + + private static final String TOSTRING_XPATH = "//PrimaryExpression[PrimaryPrefix[Name" + + "[(ends-with(@Image, '.toString'))]][" + + "(../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral" + + "='true'])" + " and ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 )]]" + + "[not(ancestor::Expression/ConditionalAndExpression//EqualityExpression[@Image='!=']//NullLiteral)]" + + "[not(ancestor::Expression/ConditionalOrExpression//EqualityExpression[@Image='==']//NullLiteral)]"; + + private static final String LOMBOK_XPATH = "../Annotation/MarkerAnnotation/Name[" + + "(@Image='Data' and //ImportDeclaration[@ImportedName='lombok.Data' or @ImportedName='lombok'])" + + " or (@Image='ToString' and //ImportDeclaration[@ImportedName='lombok.ToString' or @ImportedName='lombok'])" + + " or (@Image='lombok.Data') or (@Image='lombok.ToString')]"; + + private static final String MESSAGE_KEY_PREFIX = "java.oop.PojoMustOverrideToStringRule.violation.msg"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (!isPojo(node)) { + return super.visit(node, data); + } + if (node.isAbstract() || withLombokAnnotation(node)) { + return super.visit(node, data); + } + + if (!node.hasDescendantMatchingXPath(XPATH)) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage(MESSAGE_KEY_PREFIX + ".notostring", node.getImage())); + } else { + checkForExtend(node, data); + } + return super.visit(node, data); + } + + private void checkForExtend(ASTClassOrInterfaceDeclaration node, Object data) { + /* + * The super.toString method should be called in front of the whole implementation + * if the current class extends another POJO class + * -> this part not checked + */ + ASTExtendsList extendsList = node.getFirstChildOfType(ASTExtendsList.class); + if (extendsList == null) { + return; + } + String baseName = extendsList.getFirstChildOfType(ASTClassOrInterfaceType.class).getImage(); + if (!PojoUtils.isPojo(baseName)) { + return; + } + try { + // toString() definition + ASTMethodDeclaration toStringMethod = (ASTMethodDeclaration)node.findChildNodesWithXPath(XPATH).get(0); + ASTBlock block = toStringMethod.getBlock(); + if (block.hasDescendantMatchingXPath(TOSTRING_XPATH)) { + addViolationWithMessage(data, block, MESSAGE_KEY_PREFIX + ".usesuper"); + } + } catch (JaxenException e) { + throw new RuntimeException("XPath expression " + XPATH + " failed: " + e.getLocalizedMessage(), e); + } + } + + /** + * Class with lombok @Data will be skipped + */ + private boolean withLombokAnnotation(ASTClassOrInterfaceDeclaration node) { + return node.hasDescendantMatchingXPath(LOMBOK_XPATH); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustUsePrimitiveFieldRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustUsePrimitiveFieldRule.java new file mode 100644 index 0000000..a383024 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoMustUsePrimitiveFieldRule.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractPojoRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTType; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Rules for using primitive data types and wrapper classes: + * 1) Members of a POJO class must be wrapper classes. + * 2) The return value and arguments of a RPC method must be wrapper classes. + * 3) [Recommended] Local variables should be primitive data types. + * + * check only 1) here + * + * @author zenghou.fw + * @date 2016/11/25 + */ +public class PojoMustUsePrimitiveFieldRule extends AbstractPojoRule { + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (!isPojo(node)) { + return super.visit(node, data); + } + try { + List fields = node.findChildNodesWithXPath( + "ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration"); + + for (Node fieldNode : fields) { + ASTFieldDeclaration field = (ASTFieldDeclaration)fieldNode; + boolean shouldProcess = !field.isPublic() && !field.isStatic() && !field.isTransient(); + if (!shouldProcess) { + continue; + } + Class type = field.getType(); + // TODO works only in current compilation file, by crossing files will be null + if (type != null && type.isPrimitive()) { + addViolationWithMessage(data, field.getFirstDescendantOfType(ASTType.class), + "java.oop.PojoMustUsePrimitiveFieldRule.violation.msg", + new Object[] {field.getVariableName()}); + } + } + } catch (JaxenException e) { + throw new RuntimeException(e.getMessage(), e); + } + + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoNoDefaultValueRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoNoDefaultValueRule.java new file mode 100644 index 0000000..f409344 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/PojoNoDefaultValueRule.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import java.util.List; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractPojoRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer; +import org.jaxen.JaxenException; + +/** + * [Mandatory] While defining POJO classes like DO, DTO, VO, etc., do not assign any default values to the members. + * + * @author zenghou.fw + * @date 2016/11/22 + */ +public class PojoNoDefaultValueRule extends AbstractPojoRule { + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (!isPojo(node)) { + return super.visit(node, data); + } + try { + List fields = node.findChildNodesWithXPath( + "ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration"); + + for (Node fieldNode : fields) { + ASTFieldDeclaration field = (ASTFieldDeclaration)fieldNode; + boolean shouldProcess = !field.isPublic() && !field.isFinal() && !field.isStatic() && !field + .isVolatile() && + field.hasDescendantOfType(ASTVariableInitializer.class); + if (!shouldProcess) { + continue; + } + ViolationUtils.addViolationWithPrecisePosition(this, field, data, + I18nResources.getMessage("java.oop.PojoNoDefaultValueRule.violation.msg", + field.getVariableName())); + } + } catch (JaxenException e) { + throw new RuntimeException(e.getMessage(), e); + } + + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/StringConcatRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/StringConcatRule.java new file mode 100644 index 0000000..9583c94 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/StringConcatRule.java @@ -0,0 +1,138 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import java.util.List; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.NumberConstants; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; +import net.sourceforge.pmd.lang.java.ast.ASTDoStatement; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; +import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement; +import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode; +import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import org.jaxen.JaxenException; + +/** + * [Recommended] Use the append method in StringBuilder inside a loop body when concatenating multiple strings. + * + * @author zenghou.fw + * @date 2017/04/11 + */ +public class StringConcatRule extends AbstractAliRule { + + private static final String XPATH = + "Statement/Block//Expression[preceding-sibling::AssignmentOperator]/AdditiveExpression[(@Image = '+') and " + + "count(./PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral = 'true']) > 0]"; + + @Override + public Object visit(ASTForStatement node, Object data) { + checkStringConcat(node, data, ASTForStatement.class); + return super.visit(node, data); + } + + @Override + public Object visit(ASTWhileStatement node, Object data) { + checkStringConcat(node, data, ASTWhileStatement.class); + return super.visit(node, data); + } + + @Override + public Object visit(ASTDoStatement node, Object data) { + checkStringConcat(node, data, ASTDoStatement.class); + return super.visit(node, data); + } + + /** + * Find additive assignment with string literal, then check if the assigned variable defined out of the loop, + * + * @param node + * @param data + * @param nodeClass + */ + private void checkStringConcat(Node node, Object data, Class nodeClass) { + try { + List additiveNodes = node.findChildNodesWithXPath(XPATH); + for (Node additiveNode : additiveNodes) { + ASTAdditiveExpression additiveExpression = (ASTAdditiveExpression)additiveNode; + Node assignmentStatement = additiveExpression.getNthParent(2); + if (!(assignmentStatement instanceof ASTStatementExpression)) { + continue; + } + List nodes = ((ASTStatementExpression)assignmentStatement) + .findChildNodesWithXPath("PrimaryExpression/PrimaryPrefix/Name[@Image]"); + if (nodes == null || nodes.size() != NumberConstants.INTEGER_SIZE_OR_LENGTH_1) { + continue; + } + NameDeclaration resultVar = ((ASTName)nodes.get(0)).getNameDeclaration(); + if (resultVar != null && resultVar.getNode() != null) { + boolean isDefinedInLoop = false; + + AbstractJavaNode loopStatement = (AbstractJavaNode)resultVar.getNode().getFirstParentOfType( + nodeClass); + + while (loopStatement != null) { + if (loopStatement == node) { + isDefinedInLoop = true; + break; + } + loopStatement = (AbstractJavaNode)loopStatement.getFirstParentOfType(nodeClass); + } + + // if assigned variable defined in the loop then break + if (isDefinedInLoop) { + return; + } + } + // arguments joint by "+" + for (int i = 0; i < additiveNode.jjtGetNumChildren(); i++) { + Node firstArg = additiveNode.jjtGetChild(i); + if (!(firstArg instanceof ASTPrimaryExpression)) { + continue; + } + List names = ((ASTPrimaryExpression)firstArg). + findChildNodesWithXPath("./PrimaryPrefix/Name[@Image]"); + if (names == null || names.size() != NumberConstants.INTEGER_SIZE_OR_LENGTH_1) { + continue; + } + NameDeclaration firstArgVar = ((ASTName)names.get(0)).getNameDeclaration(); + + // concat self, e.g. a = a + b; + if (resultVar == firstArgVar) { + addViolation(data, additiveNode); + break; + } + } + } + } catch (JaxenException e) { + e.printStackTrace(); + } + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.oop.PojoMustOverrideToStringRule.violation.msg")); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java new file mode 100644 index 0000000..6a262d7 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/oop/WrapperTypeEqualityRule.java @@ -0,0 +1,57 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.rule.util.NodeUtils; +import com.alibaba.p3c.pmd.lang.java.util.NumberConstants; + +import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; + +/** + * [Mandatory] The wrapper classes should be compared by equals method rather than by symbol of '==' directly. + * + * @author zenghou.fw + * @date 2016/11/22 + */ +public class WrapperTypeEqualityRule extends AbstractAliRule { + + @Override + public Object visit(ASTEqualityExpression node, Object data) { + final String literalPrefix = "PrimaryExpression/PrimaryPrefix/Literal"; + final String unaryExpression = "UnaryExpression"; + // null presents in either side of "==" or "!=" means no violation + if (node.hasDescendantMatchingXPath(literalPrefix) + || node.hasDescendantMatchingXPath(unaryExpression)) { + return super.visit(node, data); + } + + // possible elements around "==" are PrimaryExpression or UnaryExpression(e.g. a == -2) + List expressions = node.findChildrenOfType(ASTPrimaryExpression.class); + if (expressions.size() == NumberConstants.INTEGER_SIZE_OR_LENGTH_2) { + if (NodeUtils.isWrapperType(expressions.get(0)) && + NodeUtils.isWrapperType(expressions.get(1))) { + addViolationWithMessage(data, node, "java.oop.WrapperTypeEqualityRule.violation.msg"); + } + } + + return super.visit(node, data); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/orm/IbatisMethodQueryForListRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/orm/IbatisMethodQueryForListRule.java new file mode 100644 index 0000000..86e4030 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/orm/IbatisMethodQueryForListRule.java @@ -0,0 +1,188 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.orm; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import org.apache.commons.lang3.StringUtils; +import org.jaxen.JaxenException; + +/** + * [Mandatory] iBatis built in queryForList(String statementName, int start, int size) is not recommended. + * Note: It may lead to OOM issue because its implementation is to retrieve all DB records of statementName's + * corresponding SQL statement, then start, size subset is applied through subList. + * + * @author changle.lq + * @date 2017/04/16 + */ +public class IbatisMethodQueryForListRule extends AbstractAliRule { + private static final String SQL_MAP_CLIENT_IMPORT_FULL_NAME = "com.ibatis.sqlmap.client.SqlMapClient"; + private static final String SQL_MAP_CLIENT_IMPORT_SIMPLE_NAME = "com.ibatis.sqlmap.client.*"; + private static final String SQL_MAP_CLIENT_NAME = "SqlMapClient"; + private static final String IBATIS_QUERY_FOR_LIST_METHOD_NAME = ".queryForList"; + private static final String PRIMARY_METHOD_NAME_XPATH = "PrimaryPrefix/Name"; + private static final String PRIMARY_METHOD_ARGUMENT_XPATH + = "PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal"; + private static final String FIELDS_XPATH = "ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration"; + + @Override + public Object visit(ASTCompilationUnit node, Object data) { + boolean hasImportSqlMapClient = hasSqlMapClientImport(node.findChildrenOfType(ASTImportDeclaration.class)); + if (!hasImportSqlMapClient) { + return super.visit(node, data); + } + List classOrInterfaceDeclarations + = node.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); + if (classOrInterfaceDeclarations == null || classOrInterfaceDeclarations.isEmpty()) { + return super.visit(node, data); + } + for (ASTClassOrInterfaceDeclaration classOrInterfaceDeclaration : classOrInterfaceDeclarations) { + visitASTClassOrInterfaceDeclaration(classOrInterfaceDeclaration, data); + } + return super.visit(node, data); + } + + private void visitASTClassOrInterfaceDeclaration(ASTClassOrInterfaceDeclaration classOrInterfaceDeclaration, + Object data) { + try { + List fieldDeclarations = classOrInterfaceDeclaration.findChildNodesWithXPath(FIELDS_XPATH); + Set sqlMapFields = getSqlMapFields(fieldDeclarations); + if (sqlMapFields.isEmpty()) { + return; + } + List primaryExpressions = classOrInterfaceDeclaration.findDescendantsOfType( + ASTPrimaryExpression.class); + for (ASTPrimaryExpression primaryExpression : primaryExpressions) { + visitPrimaryExpression(primaryExpression, data, sqlMapFields); + } + } catch (JaxenException ignored) { + } + } + + private Set getSqlMapFields(List fieldDeclarations) { + if (fieldDeclarations == null || fieldDeclarations.isEmpty()) { + return Collections.emptySet(); + } + Set set = new HashSet<>(); + for (Node node : fieldDeclarations) { + ASTFieldDeclaration fieldDeclaration = (ASTFieldDeclaration)node; + if (sqlMapClientField(fieldDeclaration)) { + set.add(fieldDeclaration.getVariableName()); + } + } + return set; + } + + /** + * if there is no introduction of related packages, skip the inspection + * The import statement is generally at the start of the class, do some cleaning + */ + private boolean hasSqlMapClientImport(List importDeclarations) { + if (importDeclarations == null || importDeclarations.isEmpty()) { + return false; + } + for (ASTImportDeclaration importDeclaration : importDeclarations) { + ASTName astName = importDeclaration.getFirstChildOfType(ASTName.class); + boolean hasImport = astName != null && (SQL_MAP_CLIENT_IMPORT_FULL_NAME.equals(astName.getImage()) + || SQL_MAP_CLIENT_IMPORT_SIMPLE_NAME.equals(astName.getImage())); + if (hasImport) { + return true; + } + } + return false; + } + + /** + * check if any method matches queryForList(String statementName,int start,int size) + */ + private void visitPrimaryExpression(ASTPrimaryExpression node, Object data, + Set sqlMapFields) throws JaxenException { + List astNames = node.findChildNodesWithXPath(PRIMARY_METHOD_NAME_XPATH); + for (Node astName : astNames) { + String methodName = astName.getImage(); + //method name not match + if (!(StringUtils.isNotEmpty(methodName) && methodName.contains( + IBATIS_QUERY_FOR_LIST_METHOD_NAME))) { + continue; + } + String methodInvokeName = methodName.substring(0, + methodName.indexOf(IBATIS_QUERY_FOR_LIST_METHOD_NAME)); + //the method caller is empty + if (StringUtils.isEmpty(methodInvokeName)) { + continue; + } + + //the method caller not matches SqlMapClient + if (!sqlMapFields.contains(methodInvokeName)) { + continue; + } + //method parameters not match + List literals = node.findChildNodesWithXPath(PRIMARY_METHOD_ARGUMENT_XPATH); + if (!(literals != null && (literals.size() == 3))) { + continue; + } + boolean firstMethodArgumentString = "java.lang.String".equals( + ((ASTLiteral)(literals.get(0))).getType().getName()); + boolean secondMethodArgumentInt = "int".equals( + ((ASTLiteral)(literals.get(1))).getType().getName()); + boolean thirdMethodArgumentInt = "int".equals( + ((ASTLiteral)(literals.get(2))).getType().getName()); + //if the parameter name and method name all matching, that is a violation of the rules + if (firstMethodArgumentString && secondMethodArgumentInt + && thirdMethodArgumentInt) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.naming.IbatisMethodQueryForListRule.violation.msg")); + } + + } + } + + /** + * if the attributes of a class defines the SqlMapClient object,collect these object name + */ + private boolean sqlMapClientField(ASTFieldDeclaration node) { + try { + List astClassOrInterfaceTypes = node.findChildNodesWithXPath( + "Type/ReferenceType/ClassOrInterfaceType"); + //find the SqlMapClient attribute node, collect these node's parent to sqlMapClientTypeFieldList + for (Node astClassOrInterfaceType : astClassOrInterfaceTypes) { + String fieldTypeName = astClassOrInterfaceType.getImage(); + if (SQL_MAP_CLIENT_NAME.equals(fieldTypeName) + || SQL_MAP_CLIENT_IMPORT_FULL_NAME.equals(fieldTypeName)) { + return true; + } + } + } catch (JaxenException ignore) { + } + return false; + + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidApacheBeanUtilsCopyRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidApacheBeanUtilsCopyRule.java new file mode 100644 index 0000000..4ad8568 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidApacheBeanUtilsCopyRule.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.other; + +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * Avoid using *Apache Beanutils* to copy attributes. + * Note: *Spring BeanUtils* and *Cglib BeanCopier* are recommended to be used, which have better performance. + * + * @author keriezhang + * @date 2016/12/14 + * + */ +public class AvoidApacheBeanUtilsCopyRule extends AbstractXpathRule { + private static final String XPATH = + "//PrimaryPrefix/Name[@Image='BeanUtils.copyProperties' and " + + "//ImportDeclaration[@ImportedName='org.apache.commons.beanutils.BeanUtils']]"; + + public AvoidApacheBeanUtilsCopyRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidMissUseOfMathRandomRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidMissUseOfMathRandomRule.java new file mode 100644 index 0000000..3d22a78 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidMissUseOfMathRandomRule.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.other; + +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * [Mandatory] The return type of Math.random() is double, value range is 0<=x<1 (0 is possible). + * If a random integer is required, do not multiply x by 10 then round the result. + * The correct way is to use nextInt or nextLong method which belong to Random Object. + * + * @author keriezhang + * @date 2017/04/14 + */ +public class AvoidMissUseOfMathRandomRule extends AbstractXpathRule { + + private static final String XPATH = + "//PrimaryExpression[./PrimaryPrefix/Name[@Image='Math.random'] and " + + "../../MultiplicativeExpression/PrimaryExpression/PrimaryPrefix/Literal[matches(@Image, '^\\d+$')] and " + + "../../../../../../CastExpression/Type/PrimitiveType[matches(@Image, '^(int|long)$')]" + + "]"; + + public AvoidMissUseOfMathRandomRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidNewDateGetTimeRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidNewDateGetTimeRule.java new file mode 100644 index 0000000..49e72b7 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidNewDateGetTimeRule.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.other; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * Use System.currentTimeMillis() to get the current millisecond. Do not use new Date().getTime(). + * + * @author keriezhang + * @date 2016/12/14 + */ +public class AvoidNewDateGetTimeRule extends AbstractXpathRule { + + private static final String XPATH = + "//PrimaryExpression" + + "[" + + "PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[@Image='Date'] and " + + "PrimaryPrefix/AllocationExpression/Arguments[@ArgumentCount=0] and " + + "PrimarySuffix[@Image='getTime'] and " + + "PrimarySuffix/Arguments[@ArgumentCount=0]" + + "]"; + + public AvoidNewDateGetTimeRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.other.AvoidNewDateGetTimeRule.violation.msg")); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidPatternCompileInMethodRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidPatternCompileInMethodRule.java new file mode 100644 index 0000000..f571817 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/other/AvoidPatternCompileInMethodRule.java @@ -0,0 +1,57 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.other; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; +import com.alibaba.p3c.pmd.lang.java.util.ViolationUtils; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; + +/** + * When using regex, precompile needs to be done in order to increase the matching performance. + * Note: Do not define Pattern pattern = Pattern.compile(.); within method body. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class AvoidPatternCompileInMethodRule extends AbstractXpathRule { + /** + * The parameter of Pattern.compile cannot be a string literal. + */ + private static final String XPATH = "//MethodDeclaration//PrimaryExpression[" + + "PrimaryPrefix/Name[@Image='Pattern.compile'] and " + + "PrimarySuffix/Arguments/ArgumentList/Expression/" + + "PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral='true']]"; + + public AvoidPatternCompileInMethodRule() { + setXPath(XPATH); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + ASTLocalVariableDeclaration localVariableDeclaration = node.getFirstParentOfType( + ASTLocalVariableDeclaration.class); + if (localVariableDeclaration == null) { + super.addViolation(data, node, arg); + } else { + ViolationUtils.addViolationWithPrecisePosition(this, node, data, + I18nResources.getMessage("java.other.AvoidPatternCompileInMethodRule.violation.msg", + localVariableDeclaration.getVariableName())); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithSubListToArrayListRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithSubListToArrayListRule.java new file mode 100644 index 0000000..e199059 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithSubListToArrayListRule.java @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Do not cast subList in class ArrayList, otherwise ClassCastException will be + * thrown:java.util.RandomAccessSubList + * cannot be cast to java.util.ArrayList ; + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class ClassCastExceptionWithSubListToArrayListRule extends AbstractAliRule { + + private static final String XPATH = + "//CastExpression[Type/ReferenceType/ClassOrInterfaceType[@Image = " + + "\"ArrayList\"]]/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image,'.subList')]"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isInterface()) { + return data; + } + try { + List nodes = node.findChildNodesWithXPath(XPATH); + for (Node item : nodes) { + if (!(item instanceof ASTName)) { + continue; + } + addViolationWithMessage(data, item, + "java.set.ClassCastExceptionWithSubListToArrayListRule.violation.msg", + new Object[] {item.getImage()}); + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java new file mode 100644 index 0000000..df2d890 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ClassCastExceptionWithToArrayRule.java @@ -0,0 +1,84 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; +import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Do not use toArray method without arguments. Since the return type is Object[], ClassCastException will + * be thrown when casting it to a different array type。 + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class ClassCastExceptionWithToArrayRule extends AbstractAliRule { + + private static final String XPATH + = "//CastExpression[Type/ReferenceType/ClassOrInterfaceType[@Image !=\"Object\"]]/PrimaryExpression"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isInterface()) { + return data; + } + try { + List nodes = node.findChildNodesWithXPath(XPATH); + for (Node item : nodes) { + if (!(item instanceof ASTPrimaryExpression)) { + continue; + } + ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression)item; + List primaryPrefixs = + primaryExpression.findChildrenOfType(ASTPrimaryPrefix.class); + List primarySuffixs = + primaryExpression.findChildrenOfType(ASTPrimarySuffix.class); + if (primaryPrefixs == null || primarySuffixs == null || primaryPrefixs.isEmpty() + || primarySuffixs.isEmpty()) { + continue; + } + ASTPrimaryPrefix prefix = primaryPrefixs.get(0); + ASTPrimarySuffix suffix = primarySuffixs.get(0); + if (prefix.jjtGetNumChildren() == 0) { + continue; + } + Node prefixChildNode = prefix.jjtGetChild(0); + String childName = prefixChildNode.getImage(); + if (childName == null) { + continue; + } + if (childName.endsWith(".toArray") && suffix.getArgumentCount() == 0 + && primarySuffixs.size() == 1) { + addViolationWithMessage(data, item, + "java.set.ClassCastExceptionWithSubListToArrayListRule.violation.msg", + new Object[] {childName}); + } + } + + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/CollectionInitShouldAssignCapacityRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/CollectionInitShouldAssignCapacityRule.java new file mode 100644 index 0000000..4b3c739 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/CollectionInitShouldAssignCapacityRule.java @@ -0,0 +1,81 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTArguments; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import org.jaxen.JaxenException; + +/** + * [Recommended] Set a size when initializing a collection if possible. + * + * @author shengfang.gsf + * @date 2017/04/06 + */ +public class CollectionInitShouldAssignCapacityRule extends AbstractAliRule { + + /** + * Black List,will increase ArrayList, HashSet etc follow-up + */ + private final static List COLLECTION_LIST = NameListConfig.NAME_LIST_SERVICE + .getNameList(CollectionInitShouldAssignCapacityRule.class.getSimpleName(), "COLLECTION_TYPE"); + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + try { + // find Collection initialization + for (String collectionType : COLLECTION_LIST) { + visitByCollections(node, data, collectionType); + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } + + private void visitByCollections(ASTClassOrInterfaceDeclaration node, Object data, String collectionType) + throws JaxenException { + String collectionArgXpath = + "//AllocationExpression/ClassOrInterfaceType[@Image='" + collectionType + "']/../Arguments"; + List argumentsNodes = node.findChildNodesWithXPath(collectionArgXpath); + + for (Node argNode : argumentsNodes) { + if (!(argNode instanceof ASTArguments)) { + continue; + } + // filter not inner method + if (argNode.getFirstParentOfType(ASTMethodDeclaration.class) == null) { + continue; + } + ASTArguments argumentNode = (ASTArguments)argNode; + Integer count = argumentNode.getArgumentCount(); + // judge whether parameters have initial size + if (count == 0) { + addViolationWithMessage(data, argNode, + "java.set.CollectionInitShouldAssignCapacityRule.violation.msg", + new Object[] {collectionType}); + } + } + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ConcurrentExceptionWithModifyOriginSubListRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ConcurrentExceptionWithModifyOriginSubListRule.java new file mode 100644 index 0000000..3aeaf64 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/ConcurrentExceptionWithModifyOriginSubListRule.java @@ -0,0 +1,122 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import org.jaxen.JaxenException; + +/** + * [Mandatory] When using subList, be careful to modify the size of original list. It might cause + * ConcurrentModificationException when performing traversing, adding or deleting on the subList. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class ConcurrentExceptionWithModifyOriginSubListRule extends AbstractAliRule { + + private final static String ADD = ".add"; + private final static String REMOVE = ".remove"; + private final static String CLEAR = ".clear"; + private final static String XPATH + = "//VariableDeclarator[../Type/ReferenceType/ClassOrInterfaceType[@Image='List']]/VariableInitializer" + + "/Expression/PrimaryExpression/PrimaryPrefix/Name[ends-with(@Image,'.subList')]"; + private final static String CHILD_XPATH + = "BlockStatement/Statement/StatementExpression/PrimaryExpression/PrimaryPrefix/Name"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isInterface()) { + return data; + } + try { + List nodes = node.findChildNodesWithXPath(XPATH); + for (Node item : nodes) { + if (!(item instanceof ASTName)) { + continue; + } + String valName = getBeforeSubListVal(item.getImage()); + ASTBlock blockNode = item.getFirstParentOfType(ASTBlock.class); + if (blockNode == null || valName == null) { + continue; + } + List blockNodes = blockNode.findChildNodesWithXPath(CHILD_XPATH); + + for (Node blockItem : blockNodes) { + // adding or deleting on the subList result is forbidden + if (blockItem.getBeginLine() < item.getBeginLine()) { + continue; + } + if (checkBlockNodesValid(valName, blockItem)) { + addViolationWithMessage(data, blockItem, + "java.set.ConcurrentExceptionWithModifyOriginSubListRule.violation.msg", + new Object[] {blockItem.getImage()}); + } + } + + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } + + /** + * Find subList original variable + * + * @param image + * @return + */ + private String getBeforeSubListVal(String image) { + return image == null ? null : image.substring(0, image.indexOf(".")); + } + + /** + * Only to find out whether there is any violation within the scope of the method + * + * @param variableName + * @param item + * @return + * @throws JaxenException + */ + private boolean checkBlockNodesValid(String variableName, Node item) { + if (item instanceof ASTName) { + String name = item.getImage(); + if (judgeName(name, variableName)) { + return true; + } + } + return false; + } + + /** + * judge name equals to t.add / t.remove / t.clear + * + * @param name + * @param variableName + * @return + */ + private boolean judgeName(String name, String variableName) { + return name.equals(variableName + ADD) || name.equals(variableName + REMOVE) + || name.equals(variableName + CLEAR); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/DontModifyInForeachCircleRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/DontModifyInForeachCircleRule.java new file mode 100644 index 0000000..7e09893 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/DontModifyInForeachCircleRule.java @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTForStatement; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Do not remove or add elements to a collection in a foreach loop. Please use Iterator to remove an item. + * Iterator object should be synchronized when executing concurrent operations. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class DontModifyInForeachCircleRule extends AbstractAliRule { + + private final static String ADD = ".add"; + private final static String REMOVE = ".remove"; + private final static String CLEAR = ".clear"; + private final static String XPATH = "//ForStatement/Expression/PrimaryExpression/PrimaryPrefix/Name"; + private final static String CHILD_XPATH + = "Statement/Block/BlockStatement/Statement/StatementExpression/PrimaryExpression/PrimaryPrefix/Name"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isInterface()) { + return data; + } + try { + List nodes = node.findChildNodesWithXPath(XPATH); + for (Node item : nodes) { + if (!(item instanceof ASTName)) { + continue; + } + String variableName = item.getImage(); + if (variableName == null) { + continue; + } + ASTForStatement forStatement = item.getFirstParentOfType(ASTForStatement.class); + List blockNodes = forStatement.findChildNodesWithXPath(CHILD_XPATH); + for (Node blockItem : blockNodes) { + if (!(blockItem instanceof ASTName)) { + continue; + } + if (judgeName(blockItem.getImage(), variableName)) { + addViolationWithMessage(data, blockItem, + "java.set.DontModifyInForeachCircleRule.violation.msg", + new Object[] {blockItem.getImage()}); + } + } + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } + + private boolean judgeName(String name, String variableName) { + return name != null && (name.equals(variableName + ADD) || + name.equals(variableName + REMOVE) || name.equals(variableName + CLEAR)); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/UnsupportedExceptionWithModifyAsListRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/UnsupportedExceptionWithModifyAsListRule.java new file mode 100644 index 0000000..17f48f3 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/set/UnsupportedExceptionWithModifyAsListRule.java @@ -0,0 +1,125 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import org.jaxen.JaxenException; + +/** + * [Mandatory] Do not use methods which will modify the list after using Arrays.asList to convert array to list, + * otherwise methods like add/remove/clear will throw UnsupportedOperationException. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class UnsupportedExceptionWithModifyAsListRule extends AbstractAliRule { + + private final static String ADD = ".add"; + private final static String REMOVE = ".remove"; + private final static String CLEAR = ".clear"; + private final static String XPATH + = "//VariableDeclarator[../Type/ReferenceType/ClassOrInterfaceType[@Image='List']]/VariableInitializer" + + "/Expression/PrimaryExpression/PrimaryPrefix/Name[@Image='Arrays.asList']"; + private final static String CHILD_XPATH + = "BlockStatement/Statement/StatementExpression/PrimaryExpression/PrimaryPrefix/Name"; + + @Override + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isInterface()) { + return data; + } + try { + // find Array.asList variable + List nodes = node.findChildNodesWithXPath(XPATH); + for (Node item : nodes) { + if (!(item instanceof ASTName)) { + continue; + } + List parents = + item.getParentsOfType(ASTVariableDeclarator.class); + if (parents == null || parents.size() == 0 || parents.size() > 1) { + continue; + } + ASTVariableDeclarator declarator = parents.get(0); + ASTVariableDeclaratorId variableName = + declarator.getFirstChildOfType(ASTVariableDeclaratorId.class); + + String valName = variableName.getImage(); + // find Variable scope code block + ASTBlock blockNode = variableName.getFirstParentOfType(ASTBlock.class); + if (blockNode == null || valName == null) { + continue; + } + List blockNodes = blockNode.findChildNodesWithXPath(CHILD_XPATH); + // variable.add .removed is forbbiden. + for (Node blockItem : blockNodes) { + if (blockItem.getBeginLine() < item.getBeginLine()) { + continue; + } + if (checkBlockNodesValid(valName, blockItem)) { + addViolationWithMessage(data, blockItem, + "java.set.UnsupportedExceptionWithModifyAsListRule.violation.msg", + new Object[] {blockItem.getImage()}); + } + } + + } + } catch (JaxenException e) { + e.printStackTrace(); + } + return super.visit(node, data); + } + + /** + * Only to find out whether there is any violation within the scope of the corresponding method + * + * @param variableName + * @param item + * @return + * @throws JaxenException + */ + private boolean checkBlockNodesValid(String variableName, Node item) { + if (item instanceof ASTName) { + String name = item.getImage(); + if (judgeName(name, variableName)) { + return true; + } + } + return false; + } + + /** + * judge name equels t.add t.remove t.clear + * + * @param name + * @param variableName + * @return + */ + private boolean judgeName(String name, String variableName) { + return name.equals(variableName + ADD) || name.equals(variableName + REMOVE) + || name.equals(variableName + CLEAR); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/CommentUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/CommentUtils.java new file mode 100644 index 0000000..104df11 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/CommentUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.util; + +import java.util.List; +import java.util.SortedMap; + +import net.sourceforge.pmd.lang.ast.Node; + +/** + * + * @author keriezhang + * @date 2016/11/21 + * + */ +public class CommentUtils { + + /** + * add node to SortedMap with sequence to determine comment location + * + * @param map sorted map + * @param nodes nodes + */ + public static void addNodesToSortedMap(SortedMap map, List nodes) { + for (Node node : nodes) { + // sorted by line and column + map.put((node.getBeginLine() << 16) + node.getBeginColumn(), node); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java new file mode 100644 index 0000000..13ba92b --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/rule/util/NodeUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.util; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; + +/** + * @author caikang + * @date 2016/11/16 + */ +public class NodeUtils { + public static boolean isParentOrSelf(Node descendant,Node ancestor){ + if(descendant == ancestor) { + return true; + } + if(descendant == null || ancestor == null){ + return false; + } + Node parent = descendant.jjtGetParent(); + while(parent != ancestor && parent != null){ + parent = parent.jjtGetParent(); + } + return parent == ancestor; + } + + /** + * TODO optimize + * @param expression expression + * @return true if wrapper type + */ + public static boolean isWrapperType(ASTPrimaryExpression expression) { + return TypeHelper.isA(expression, Integer.class) + || TypeHelper.isA(expression, Long.class) + || TypeHelper.isA(expression, Boolean.class) + || TypeHelper.isA(expression, Byte.class) + || TypeHelper.isA(expression, Double.class) + || TypeHelper.isA(expression, Short.class) + || TypeHelper.isA(expression, Float.class) + || TypeHelper.isA(expression, Character.class); + } + + public static boolean isConstant(ASTFieldDeclaration field) { + return field != null && field.isStatic() && field.isFinal(); + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/GeneratedCodeUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/GeneratedCodeUtils.java new file mode 100644 index 0000000..69c5a53 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/GeneratedCodeUtils.java @@ -0,0 +1,57 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +import java.util.List; + +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; + +/** + * @author caikang + * @date 2017/06/21 + */ +public class GeneratedCodeUtils { + private static final String ANNOTATION_NAME = "javax.annotation.Generated"; + + private static final String CLASS = "class"; + + public static boolean isGenerated(ASTCompilationUnit compilationUnit) { + List importDeclarationList + = compilationUnit.findChildrenOfType(ASTImportDeclaration.class); + if (importDeclarationList.isEmpty()) { + return false; + } + + for (ASTImportDeclaration importDeclaration : importDeclarationList) { + if (ANNOTATION_NAME.equals(importDeclaration.getImportedName())) { + return true; + } + } + return false; + } + + public static boolean isGenerated(String content) { + int classIndex = content.indexOf(CLASS); + if (classIndex <= 1) { + return false; + } + //most of file is not generated + String importHeader = content.substring(0, classIndex); + return importHeader.contains(ANNOTATION_NAME); + } +} + diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/NumberConstants.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/NumberConstants.java new file mode 100644 index 0000000..d6737fc --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/NumberConstants.java @@ -0,0 +1,31 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +/** + * @author caikang + * @date 2016/12/28 + */ +public interface NumberConstants { + int INTEGER_SIZE_OR_LENGTH_0 = 0; + int INTEGER_SIZE_OR_LENGTH_1 = 1; + int INTEGER_SIZE_OR_LENGTH_2 = 2; + int INTEGER_SIZE_OR_LENGTH_3 = 3; + + int INDEX_0 = 0; + int INDEX_1 = 1; + int INDEX_2 = 2; +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/PojoUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/PojoUtils.java new file mode 100644 index 0000000..586a7a4 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/PojoUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +import java.util.List; + +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; + +/** + * POJO Utils + * + * @author zenghou.fw + * @date 2016/11/25 + */ +public class PojoUtils { + private static final List POJO_SUFFIX_SET = + NameListConfig.NAME_LIST_SERVICE.getNameList("PojoMustOverrideToStringRule", "POJO_SUFFIX_SET"); + + private PojoUtils() { + } + + public static boolean isPojo(String klass) { + if (klass == null) { + return false; + } + for (String suffix : POJO_SUFFIX_SET) { + if (klass.endsWith(suffix)) { + return true; + } + } + return false; + } + + public static boolean isPojo(ASTClassOrInterfaceDeclaration node) { + return node != null && isPojo(node.getImage()); + } + +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/SpiLoader.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/SpiLoader.java new file mode 100644 index 0000000..e6f4f66 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/SpiLoader.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.p3c.pmd.lang.java.util.namelist.NameListConfig; + +/** + * @author changle.lq + * @date 2017/04/01 + */ +public class SpiLoader { + private final static ConcurrentHashMap, Object> INSTANCE_CACHE = new ConcurrentHashMap, Object>(); + + @SuppressWarnings("unchecked") + public static T getInstance(Class classType) { + T instance = (T)INSTANCE_CACHE.get(classType); + + if (instance != null) { + return instance; + } + try { + instance = ServiceLoader.load(classType, NameListConfig.class.getClassLoader()).iterator().next(); + if (instance == null) { + return null; + } + INSTANCE_CACHE.putIfAbsent(classType, instance); + return instance; + } catch (Throwable e) { + return null; + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/StringAndCharConstants.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/StringAndCharConstants.java new file mode 100644 index 0000000..a0fe3cf --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/StringAndCharConstants.java @@ -0,0 +1,24 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +/** + * @author caikang + * @date 2017/03/28 + */ +public interface StringAndCharConstants { + char DOT = '.'; +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/ViolationUtils.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/ViolationUtils.java new file mode 100644 index 0000000..49aed74 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/ViolationUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.rule.AbstractRule; + +/** + * @author caikang + * @date 2017/01/14 + */ +public class ViolationUtils { + public static void addViolationWithPrecisePosition(AbstractRule rule, Node node, Object data) { + addViolationWithPrecisePosition(rule, node, data, null); + } + + public static void addViolationWithPrecisePosition(AbstractRule rule, Node node, Object data, + String message) { + if (node instanceof ASTFieldDeclaration) { + ASTVariableDeclaratorId variableDeclaratorId = node.getFirstDescendantOfType(ASTVariableDeclaratorId.class); + addViolation(rule, variableDeclaratorId, data, message); + return; + } + if (node instanceof ASTMethodDeclaration) { + ASTMethodDeclarator declarator = node.getFirstChildOfType(ASTMethodDeclarator.class); + addViolation(rule, declarator, data, message); + return; + } + addViolation(rule, node, data, message); + } + + private static void addViolation(AbstractRule rule, Node node, Object data, String message) { + if (message == null) { + rule.addViolation(data, node); + } else { + rule.addViolationWithMessage(data, node, message); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListConfig.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListConfig.java new file mode 100644 index 0000000..ba454fb --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListConfig.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util.namelist; + +import com.alibaba.p3c.pmd.lang.java.util.SpiLoader; + +/** + * @author changle.lq + * @date 2017/03/27 + */ +public class NameListConfig { + public static final NameListService NAME_LIST_SERVICE = getNameListService(); + + private static NameListService getNameListService() { + NameListService instance = SpiLoader.getInstance(NameListService.class); + if (instance == null) { + instance = new NameListServiceImpl(); + } + return instance; + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListService.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListService.java new file mode 100644 index 0000000..381e3a4 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListService.java @@ -0,0 +1,45 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util.namelist; + +import java.util.List; +import java.util.Map; + +/** + * @author changle.lq + * @date 2017/03/23 + */ +public interface NameListService { + /** + * get name list + * @param className class name + * @param name type name + * @return name list + */ + List getNameList(String className,String name); + + /** + * get config + * @param className class name + * @param name type name + * @param kClass type of key + * @param vClass type of value + * @param type of key + * @param type of value + * @return name list + */ + Map getNameMap(String className,String name,Class kClass,Class vClass); +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListServiceImpl.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListServiceImpl.java new file mode 100644 index 0000000..6b8ba98 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/java/util/namelist/NameListServiceImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.util.namelist; + +import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; + +/** + * @author changle.lq + * @date 2017/03/27 + */ +public class NameListServiceImpl implements NameListService { + + private static final String NAME_LIST_PROPERTY_FILE_NAME = "namelist.properties"; + private static final Properties PROPERTIES = initProperties(); + private static final String SEPARATOR = "_"; + + private static Properties initProperties() { + LinkedProperties props = new LinkedProperties(); + ClassLoader classLoader = NameListServiceImpl.class.getClassLoader(); + try { + props.load(classLoader.getResourceAsStream(NAME_LIST_PROPERTY_FILE_NAME)); + } catch (IOException ex) { + throw new IllegalStateException("Load namelist.properties fail", ex); + } + return props; + } + + @Override + public List getNameList(String className, String name) { + return JSON.parseArray((String)PROPERTIES.get(className + SEPARATOR + name), String.class); + } + + @Override + public Map getNameMap(String className, String name, Class kClass, Class vClass) { + return JSON.parseObject((String)PROPERTIES.get(className + SEPARATOR + name), + new TypeReference>(kClass, vClass) {}); + } + + private static class LinkedProperties extends Properties { + private LinkedHashSet linkedKeys = new LinkedHashSet<>(); + + @Override + public Object put(Object key, Object value) { + linkedKeys.add(key); + return super.put(key, value); + } + + public int getSize() { + return linkedKeys.size(); + } + } +} diff --git a/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/vm/rule/other/UseQuietReferenceNotationRule.java b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/vm/rule/other/UseQuietReferenceNotationRule.java new file mode 100644 index 0000000..736b7b3 --- /dev/null +++ b/p3c-pmd/src/main/java/com/alibaba/p3c/pmd/lang/vm/rule/other/UseQuietReferenceNotationRule.java @@ -0,0 +1,112 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.vm.rule.other; + +import java.util.List; +import java.util.regex.Pattern; + +import com.alibaba.p3c.pmd.I18nResources; +import com.alibaba.p3c.pmd.lang.AbstractXpathRule; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vm.ast.ASTDirective; +import net.sourceforge.pmd.lang.vm.ast.AbstractVmNode; +import net.sourceforge.pmd.lang.vm.ast.Token; +import net.sourceforge.pmd.lang.vm.ast.VmParserConstants; + +/** + * [Mandatory] Variables must add exclamatory mark when passing to velocity engine from backend, like $!{var}. + * Note: If attribute is null or does not exist, ${var} will be shown directly on web pages. + * + * @author keriezhang + * @date 2016/12/14 + */ +public class UseQuietReferenceNotationRule extends AbstractXpathRule { + /** + * scan file path pattern + */ + private static final Pattern ALLOW_FILE_PATTERN = Pattern.compile(".*(template|velocity).*"); + + private static final String UT_FILE_NAME = "n/a"; + private static final String MACRO_NAME = "macro"; + + /** + * Check reference between two text nodes. Exclude references scan in method. + */ + private static final String XPATH = + "//Reference[matches(@literal, \"^\\$[^!]+\") and ./preceding-sibling::Text and ./following-sibling::Text]"; + + public UseQuietReferenceNotationRule() { + setXPath(XPATH); + } + + @Override + public void evaluate(Node node, RuleContext ctx) { + // Exclude directories other than template and velocity. + String sourceCodeFilename = ctx.getSourceCodeFilename(); + + // If file path is not n/a(unit test),and does not contain 'template', 'velocity',then skip it。 + if (!UT_FILE_NAME.equals(sourceCodeFilename) && !ALLOW_FILE_PATTERN.matcher(sourceCodeFilename).matches()) { + return; + } + + // Exclude references inside macro. + if (checkMacro(node)) { + return; + } + + super.evaluate(node, ctx); + } + + @Override + public void addViolation(Object data, Node node, String arg) { + String name = getIdentifyName((AbstractVmNode)node); + String text = I18nResources.getMessage("vm.other.UseQuietReferenceNotationRule.violation.msg", name); + addViolationWithMessage(data, node, text); + } + + private String getIdentifyName(AbstractVmNode node) { + Token token = node.getFirstToken(); + StringBuilder sb = new StringBuilder(); + while (token.kind >= VmParserConstants.IDENTIFIER && token.kind < VmParserConstants.RCURLY) { + if (token.kind != VmParserConstants.LCURLY) { + sb.append(token.image); + } + token = token.next; + } + return sb.toString(); + } + + /** + * Check if reference is inside macro. + * + * @param node node + * @return true/false + */ + private boolean checkMacro(Node node) { + List directiveParents = node.getParentsOfType(ASTDirective.class); + + for (ASTDirective directiveParent : directiveParents) { + if (MACRO_NAME.equals(directiveParent.getDirectiveName())) { + return true; + } + } + + return false; + } + +} diff --git a/p3c-pmd/src/main/resources/META-INF/services/com.alibaba.p3c.pmd.lang.java.util.namelist.NameListService b/p3c-pmd/src/main/resources/META-INF/services/com.alibaba.p3c.pmd.lang.java.util.namelist.NameListService new file mode 100644 index 0000000..e69de29 diff --git a/p3c-pmd/src/main/resources/messages.xml b/p3c-pmd/src/main/resources/messages.xml new file mode 100644 index 0000000..0b7a96f --- /dev/null +++ b/p3c-pmd/src/main/resources/messages.xml @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 使用ScheduledExecutorService代替Timer吧 + + 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。 + + + 【%s()】可能导致线程安全问题 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + generate toString时,如果继承了另一个POJO类,注意在前面加一下super.toString。]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/messages_en.xml b/p3c-pmd/src/main/resources/messages_en.xml new file mode 100644 index 0000000..80db028 --- /dev/null +++ b/p3c-pmd/src/main/resources/messages_en.xml @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use ScheduledExecutorService instead. + + + + + + [%s()] may cause thread-safety issues. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/p3c-pmd/src/main/resources/namelist.properties b/p3c-pmd/src/main/resources/namelist.properties new file mode 100644 index 0000000..8941bb5 --- /dev/null +++ b/p3c-pmd/src/main/resources/namelist.properties @@ -0,0 +1,8 @@ +ConstantFieldShouldBeUpperCaseRule_LOG_VARIABLE_TYPE_SET=["Log","Logger"] +ConstantFieldShouldBeUpperCaseRule_WHITE_LIST=["serialVersionUID"] +LowerCamelCaseVariableNamingRule_WHITE_LIST=["DAOImpl"] +PojoMustOverrideToStringRule_POJO_SUFFIX_SET=["DO","DTO","VO","BO"] +UndefineMagicConstantRule_LITERAL_WHITE_LIST=["0","1","\\\"\\\"","0.0","1.0","-1","0L","1L"] +MethodReturnWrapperTypeRule_PRIMITIVE_TYPE_TO_WAPPER_TYPE={"int":"Integer","boolean":"Boolean","float":"Float","double":"Double","byte":"Byte","short":"Short","long":"Long","char":"Character"} +CollectionInitShouldAssignCapacityRule_COLLECTION_TYPE=["HashMap","ConcurrentHashMap"] +ClassNamingShouldBeCamelRule_CLASS_NAMING_WHITE_LIST=["Hbase","HBase","ID"] diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-comment.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-comment.xml new file mode 100644 index 0000000..ae6d44f --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-comment.xml @@ -0,0 +1,153 @@ + + + + + + + java.comment.CommentsMustBeJavadocFormatRule.rule.desc + 3 + + + + */ + public Result funcA(Long ruleId, Integer page) { + return null; + } + } +]]> + + + + + java.comment.AbstractMethodOrInterfaceMethodMustUseJavadocRule.rule.desc + 3 + + + + */ + Result fetchDataByRuleId(Long ruleId, Integer page, String jsonContext); +]]> + + + + + java.comment.ClassMustHaveAuthorRule.rule.desc + 3 + + + + + + + + 2 + + + + + + + + 3 + + + + + + + + java.comment.RemoveCommentedCodeRule.rule.desc + 3 + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml new file mode 100644 index 0000000..83d3c52 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-concurrent.xml @@ -0,0 +1,288 @@ + + + + + java.concurrent.ThreadPoolCreationRule.rule.desc + 1 + + + + + (1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); + + pool.execute(()-> System.out.println(Thread.currentThread().getName())); + pool.shutdown();//gracefully shutdown + ]]> + + + + + + + + + + + + + //in code + userThreadPool.execute(thread); + ]]> + + + + 1 + + + + + + java.concurrent.AvoidManuallyCreateThreadRule.rule.desc + 2 + + (1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); + + singleThreadPool.execute(()-> System.out.println(Thread.currentThread().getName())); + singleThreadPool.shutdown(); + ]]> + + + + 2 + + (1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); + + singleThreadPool.execute(()-> System.out.println(Thread.currentThread().getName())); + singleThreadPool.shutdown(); + ]]> + + + + + + + java.concurrent.AvoidCallStaticSimpleDateFormatRule.rule.desc + + + + + + + + + DATE_FORMATTER = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd"); + } + }; + ]]> + + 2 + + + + 2 + + userThreadLocal = new ThreadLocal(); + + public static void set(User user){ + userThreadLocal.set(user); + } + + public static User get(){ + return userThreadLocal.get(); + } + + public static void remove(){ + userThreadLocal.remove(); + } + } + + /** + * @author caikang + * @date 2017/04/07 + */ + public class UserInterceptor extends HandlerInterceptorAdapter { + @Override + public boolean preHandle(HttpServletRequest request, + HttpServletResponse response, Object handler) throws Exception { + UserHolder.set(new User()); + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, + HttpServletResponse response, Object handler, Exception ex) throws Exception { + UserHolder.remove(); + } + } + ]]> + + + + 3 + + + + + + + + + java.concurrent.CountDownShouldInFinallyRule.rule.desc + 3 + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-constant.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-constant.xml new file mode 100644 index 0000000..c2937a5 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-constant.xml @@ -0,0 +1,52 @@ + + + + + + + 1 + + + + + + + + + + 3 + + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-exception.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-exception.xml new file mode 100644 index 0000000..5e88b27 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-exception.xml @@ -0,0 +1,124 @@ + + + + + + 3 + + + + + + + 2 + + + + + + + 3 + + + + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-flowcontrol.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-flowcontrol.xml new file mode 100644 index 0000000..d8daccb --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-flowcontrol.xml @@ -0,0 +1,67 @@ + + + + + + 2 + + + + + + + + 1 + + + + + + + + java.flowcontrol.AvoidComplexConditionRule.rule.desc + 3 + + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-naming.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-naming.xml new file mode 100644 index 0000000..6de3cf9 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-naming.xml @@ -0,0 +1,160 @@ + + + + + + + 3 + + + + 2 + + + + + + + 2 + + + + + + + + 3 + + + + + + + 2 + + + + 2 + + + + 2 + + + + + + + 2 + + + + + + + 3 + + + + + + + 2 + + + + + + + 3 + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-oop.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-oop.xml new file mode 100644 index 0000000..7820ab5 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-oop.xml @@ -0,0 +1,134 @@ + + + + + + 2 + + + + + + + + java.oop.WrapperTypeEqualityRule.rule.desc + 1 + + + + + + + + java.oop.PojoMustUsePrimitiveFieldRule.rule.msg.desc + 3 + + + + + + + + 3 + + + + + + + + java.oop.PojoMustOverrideToStringRule.rule.desc + 3 + + + + + + + + java.oop.StringConcatRule.rule.msg.desc + 3 + + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-orm.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-orm.xml new file mode 100644 index 0000000..7410b76 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-orm.xml @@ -0,0 +1,15 @@ + + + + + + 3 + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-other.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-other.xml new file mode 100644 index 0000000..af54d86 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-other.xml @@ -0,0 +1,82 @@ + + + + + + java.other.AvoidPatternCompileInMethodRule.rule.desc + 1 + + + + + + + java.other.AvoidApacheBeanUtilsCopyRule.rule.desc + 1 + + + + + + + java.other.AvoidNewDateGetTimeRule.rule.desc + 1 + + + + + + + 3 + + + + + + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/java/ali-set.xml b/p3c-pmd/src/main/resources/rulesets/java/ali-set.xml new file mode 100644 index 0000000..40a28c6 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/java/ali-set.xml @@ -0,0 +1,133 @@ + + + + + + 2 + + + + + + + + + + 2 + + t = Arrays.asList("a","b","c"); + //warn + t.add("22"); + //warn + t.remove("22"); + //warn + t.clear(); + ]]> + + + + + java.set.ClassCastExceptionWithSubListToArrayListRule.rule.msg.desc + 2 + + list = new ArrayList(); + list.add("22"); + //warn + List test = (ArrayList) list.subList(0, 1); + ]]> + + + list2 = new ArrayList(list.subList(0, 1)); + ]]> + + + + + 2 + + originList = new ArrayList(); + originList.add("22"); + List subList = originList.subList(0, 1); + //warn + originList.add("22"); + ]]> + + + + + + 1 + + + originList = new ArrayList(); + originList.add("22"); + for (String item : originList) { + //warn + list.add("bb"); + } + ]]> + + + it=b.iterator(); + while(it.hasNext()){ + Integer temp = it.next(); + if(delCondition){ + it.remove(); + } + + } + ]]> + + + + + java.set.CollectionInitShouldAssignCapacityRule.rule.msg.desc + 3 + + map = new HashMap(); + + ]]> + + + map = new HashMap(16); + ]]> + + + + diff --git a/p3c-pmd/src/main/resources/rulesets/vm/ali-other.xml b/p3c-pmd/src/main/resources/rulesets/vm/ali-other.xml new file mode 100644 index 0000000..cf79db3 --- /dev/null +++ b/p3c-pmd/src/main/resources/rulesets/vm/ali-other.xml @@ -0,0 +1,20 @@ + + + + + + vm.other.UseQuietReferenceNotationRule.rule.desc + 3 + + + +]]> + + + + diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentRulesTest.java new file mode 100644 index 0000000..f2d4b59 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/comment/CommentRulesTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.comment; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for comment rules. + * + * @author keriezhang + * @date 2017/06/18 + * + */ +public class CommentRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "java-ali-comment"; + + @Override + public void setUp() { + addRule(RULESET, "CommentsMustBeJavadocFormatRule"); + addRule(RULESET, "AbstractMethodOrInterfaceMethodMustUseJavadocRule"); + addRule(RULESET, "ClassMustHaveAuthorRule"); + addRule(RULESET, "EnumConstantsMustHaveCommentRule"); + addRule(RULESET, "AvoidCommentBehindStatementRule"); + addRule(RULESET, "RemoveCommentedCodeRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java new file mode 100644 index 0000000..6cff269 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/concurrent/ConcurrentRuleTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.concurrent; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for concurrent rules. + * + * @author caikang + * @date 2016/11/14 + * + */ +public class ConcurrentRuleTest extends SimpleAggregatorTst { + private static final String RULE_NAME = "java-ali-concurrent"; + + @Override + public void setUp() { + addRule(RULE_NAME, "ThreadPoolCreationRule"); + addRule(RULE_NAME, "AvoidUseTimerRule"); + addRule(RULE_NAME, "AvoidManuallyCreateThreadRule"); + addRule(RULE_NAME, "ThreadShouldSetNameRule"); + addRule(RULE_NAME, "AvoidCallStaticSimpleDateFormatRule"); + addRule(RULE_NAME, "ThreadLocalShouldRemoveRule"); + addRule(RULE_NAME, "AvoidConcurrentCompetitionRandomRule"); + addRule(RULE_NAME, "CountDownShouldInFinallyRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/constant/ConstantRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/constant/ConstantRulesTest.java new file mode 100644 index 0000000..f4d8631 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/constant/ConstantRulesTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.constant; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for constant rules. + * + * @author shengfang.gsf + * @date 2016/12/13 + * + */ +public class ConstantRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "java-ali-constant"; + + @Override + public void setUp() { + addRule(RULESET, "UpperEllRule"); + addRule(RULESET, "UndefineMagicConstantRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/exception/ExceptionRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/exception/ExceptionRulesTest.java new file mode 100644 index 0000000..98f8425 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/exception/ExceptionRulesTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.exception; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for exception rules. + * + * @author changle.lq + * @date 2016/11/23 + * + */ +public class ExceptionRulesTest extends SimpleAggregatorTst { + private static final String RULESET = "java-ali-exception"; + + @Override + public void setUp() { + addRule(RULESET, "MethodReturnWrapperTypeRule"); + addRule(RULESET, "AvoidReturnInFinallyRule"); + addRule(RULESET, "TransactionMustHaveRollbackRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/FlowControlRuleTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/FlowControlRuleTest.java new file mode 100644 index 0000000..3face90 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/FlowControlRuleTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.flowcontrol; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for flowcontrol rules. + * + * @author zenghou.fw + * @date 2017/04/11 + * + */ +public class FlowControlRuleTest extends SimpleAggregatorTst { + + + private static final String RULESET = "java-ali-flowcontrol"; + + @Override + public void setUp() { + addRule(RULESET, "SwitchStatementRule"); + addRule(RULESET, "NeedBraceRule"); + addRule(RULESET, "AvoidComplexConditionRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/naming/NamingRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/naming/NamingRulesTest.java new file mode 100644 index 0000000..501523c --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/naming/NamingRulesTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.naming; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for naming rules. + * + * @author changle.lq + * @date 2016/11/23 + * + */ +public class NamingRulesTest extends SimpleAggregatorTst { + private static final String RULESET = "java-ali-naming"; + + @Override + public void setUp() { + addRule(RULESET, "ClassNamingShouldBeCamelRule"); + addRule(RULESET, "AbstractClassShouldStartWithAbstractNamingRule"); + addRule(RULESET, "ExceptionClassShouldEndWithExceptionRule"); + addRule(RULESET, "TestClassShouldEndWithTestNamingRule"); + addRule(RULESET, "LowerCamelCaseVariableNamingRule"); + addRule(RULESET, "AvoidStartWithDollarAndUnderLineNamingRule"); + addRule(RULESET, "ConstantFieldShouldBeUpperCaseRule"); + addRule(RULESET, "ServiceOrDaoClassShouldEndWithImplRule"); + addRule(RULESET, "BooleanPropertyShouldNotStartWithIsRule"); + addRule(RULESET, "ArrayNamingShouldHaveBracketRule"); + addRule(RULESET, "PackageNamingRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/oop/OopRuleTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/oop/OopRuleTest.java new file mode 100644 index 0000000..4235795 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/oop/OopRuleTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.oop; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for oop rules. + * + * @author zenghou.fw + * @date 2016/11/29 + * + */ +public class OopRuleTest extends SimpleAggregatorTst { + + // 加载CLASSPATH下的rulesets/java/ali-oop.xml + private static final String RULESET = "java-ali-oop"; + + @Override + public void setUp() { + addRule(RULESET, "EqualsAvoidNullRule"); + addRule(RULESET, "WrapperTypeEqualityRule"); + addRule(RULESET, "PojoNoDefaultValueRule"); + addRule(RULESET, "PojoMustUsePrimitiveFieldRule"); + addRule(RULESET, "PojoMustOverrideToStringRule"); + addRule(RULESET, "StringConcatRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/orm/OrmRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/orm/OrmRulesTest.java new file mode 100644 index 0000000..010026f --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/orm/OrmRulesTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.orm; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for orm rules. + * + * @author changle.lq + * @date 2016/11/23 + * + */ +public class OrmRulesTest extends SimpleAggregatorTst { + private static final String RULESET = "java-ali-orm"; + + @Override + public void setUp() { + addRule(RULESET, "IbatisMethodQueryForListRule"); + } +} \ No newline at end of file diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/other/OtherRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/other/OtherRulesTest.java new file mode 100644 index 0000000..181c4f6 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/other/OtherRulesTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.other; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for other java rules. + * + * @author keriezhang + * @date 2017/06/18 + * + */ +public class OtherRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "java-ali-other"; + + @Override + public void setUp() { + addRule(RULESET, "AvoidApacheBeanUtilsCopyRule"); + addRule(RULESET, "AvoidNewDateGetTimeRule"); + addRule(RULESET, "AvoidPatternCompileInMethodRule"); + addRule(RULESET, "AvoidMissUseOfMathRandomRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/set/SetRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/set/SetRulesTest.java new file mode 100644 index 0000000..b96c6e3 --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/java/rule/set/SetRulesTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.java.rule.set; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for set rules. + * + * @author shengfang.gsf + * @date 2016/12/13 + */ +public class SetRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "java-ali-set"; + + @Override + public void setUp() { + addRule(RULESET, "ClassCastExceptionWithSubListToArrayListRule"); + addRule(RULESET, "ClassCastExceptionWithToArrayRule"); + addRule(RULESET, "CollectionInitShouldAssignCapacityRule"); + addRule(RULESET, "ConcurrentExceptionWithModifyOriginSubListRule"); + addRule(RULESET, "DontModifyInForeachCircleRule"); + addRule(RULESET, "UnsupportedExceptionWithModifyAsListRule"); + } +} diff --git a/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/vm/rule/other/OtherRulesTest.java b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/vm/rule/other/OtherRulesTest.java new file mode 100644 index 0000000..d6645df --- /dev/null +++ b/p3c-pmd/src/test/java/com/alibaba/p3c/pmd/lang/vm/rule/other/OtherRulesTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2017 Alibaba Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.p3c.pmd.lang.vm.rule.other; + +import net.sourceforge.pmd.testframework.SimpleAggregatorTst; + +/** + * Test for other vm rules. + * + * @author keriezhang + * @date 2017/06/18 + * + */ +public class OtherRulesTest extends SimpleAggregatorTst { + + private static final String RULESET = "vm-ali-other"; + + @Override + public void setUp() { + addRule(RULESET, "UseQuietReferenceNotationRule"); + } + +} diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AbstractMethodOrInterfaceMethodMustUseJavadocRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AbstractMethodOrInterfaceMethodMustUseJavadocRule.xml new file mode 100644 index 0000000..9c54fb4 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AbstractMethodOrInterfaceMethodMustUseJavadocRule.xml @@ -0,0 +1,214 @@ + + + + + + Non abstract method may have no comment. + 0 + + + + + + + Abstract method have no comment. + 1 + 2 + + + + + + + Abstract method have part of javadoc comment about + function, return, parameters and exceptions. + + 6 + 4 + + + + + + + Abstract method have javadoc comment about function, + return, parameters and exceptions. + + 0 + + + + + + + + Method in interface with non-javadoc comment. + + 1 + 2 + + + + + + + Void method with empty javadoc. + 1 + 4 + + + + + + + Method in interface have part of javadoc comment about + function, return, parameters and exceptions. + + 6 + 4 + + + + + + + Method in interface have javadoc comment about + function, + return, parameters and exceptions. + + 0 + + + + + + + + Interface ignore methods in inner enums or classes. + + 3 + 2,4,26 + + + + + + + Java8 Interface + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AvoidCommentBehindStatementRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AvoidCommentBehindStatementRule.xml new file mode 100644 index 0000000..69ff63b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/AvoidCommentBehindStatementRule.xml @@ -0,0 +1,62 @@ + + + + + + + + Comment before code + 0 + + + + + + + + Comments behind code + 3 + 2,4,7 + + + + + + + + Comments enum constant + 3 + 2,3,4 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/ClassMustHaveAuthorRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/ClassMustHaveAuthorRule.xml new file mode 100644 index 0000000..09be097 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/ClassMustHaveAuthorRule.xml @@ -0,0 +1,147 @@ + + + + + + + + Class without author. + 1 + 1 + + + + + + + + Class with author. + 0 + + + + + + + Class with date. + 1 + 4 + + + + + + + Class with author and date. + 0 + + + + + + + Class with inner class. + 0 + + + + + + + Enum without author + 1 + 1 + + + + + + + Enum with author + 0 + + + + + + + Enum in class + 0 + + + + + + + Enum in interface + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/CommentsMustBeJavadocFormatRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/CommentsMustBeJavadocFormatRule.xml new file mode 100644 index 0000000..cab7aab --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/CommentsMustBeJavadocFormatRule.xml @@ -0,0 +1,131 @@ + + + + + + + + Class have no comment. + 0 + + + + + + + + + Class have non-javadoc comments. + 3 + 2,4,6 + + + + + + + + Class have javadoc comments. + 0 + + + + + + + + Anonymous Inner Class have non javadoc comments. + + 2 + 1,3 + + + + + + + + Non-javadoc comments before package and import + + 0 + + + + + + + Ignore comments behind statements. + + 0 + + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/EnumConstantsMustHaveCommentRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/EnumConstantsMustHaveCommentRule.xml new file mode 100644 index 0000000..c8d51c7 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/EnumConstantsMustHaveCommentRule.xml @@ -0,0 +1,34 @@ + + + + + + + + Enum without Comment. + 1 + 2 + + + + + + + + Enum with comment. + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/RemoveCommentedCodeRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/RemoveCommentedCodeRule.xml new file mode 100644 index 0000000..8e3669f --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/comment/xml/RemoveCommentedCodeRule.xml @@ -0,0 +1,106 @@ + + + + + + + + Commented Code + 4 + 1,3,7,11 + + + + + + + + Commented code with three slashes + 0 + + + + + 100 || dtos.size() == 0) { + // return new RPCReturnResult().combineFail("invalid argument"); + // } + Collections.sort(dtos, new Comparator() { + @Override + public int compare(TicketDto o1, TicketDto o2) { + return o1.getId().compareTo(o2.getId()); + } + }); + } +} + ]]> + + + Commented code with anonymous class + 1 + 5 + + + + + + + Example in javadoc + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidCallStaticSimpleDateFormatRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidCallStaticSimpleDateFormatRule.xml new file mode 100644 index 0000000..3964f5b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidCallStaticSimpleDateFormatRule.xml @@ -0,0 +1,104 @@ + + + + AvoidCallStaticSimpleDateFormatRule + 4 + 30,22,18,45 + + + + + call with lock + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidConcurrentCompetitionRandomRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidConcurrentCompetitionRandomRule.xml new file mode 100644 index 0000000..1353f02 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidConcurrentCompetitionRandomRule.xml @@ -0,0 +1,137 @@ + + + + AvoidConcurrentCompetitionRandomRule + 1 + 13 + + + + AvoidConcurrentCompetitionRandomRule + 1 + 13 + + + + + right + 0 + + + + right + 0 + + + + Math.random + 1 + 9 + + + + Math.random + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidManuallyCreateThreadRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidManuallyCreateThreadRule.xml new file mode 100644 index 0000000..b8a823a --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidManuallyCreateThreadRule.xml @@ -0,0 +1,145 @@ + + + + AvoidManuallyCreateThreadRule + 3 + 16,19,23 + + + + right + 0 + { + Thread thread1 = new Thread(r); + thread1.setName("kdsljf"); + return thread1; + }; + + private ThreadFactory threadFactory1 = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(); + thread.setName("xxx"); + return thread; + } + }; + + private ThreadFactory threadFactory2 = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(); + return thread; + } + }.newThread(r); + } + }; + + public void testNewThread(){ + new ThreadFactory(){ + + @Override + public Thread newThread(Runnable r) { + return new Thread(); + } + }.newThread(new Runnable() { + @Override + public void run() { + + } + }); + } + + public void testParamter(){ + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + r -> { + Thread thread1 = new Thread(r); + thread1.setName("xxx"); + return thread1; + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + r -> { + Thread thread1 = new Thread(r); + thread1.setName("xxx"); + return thread1; + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(); + } + }); + } + + class MyThreadFactory implements ThreadFactory { + /** + * Constructs a new {@code Thread}. Implementations may also initialize + * priority, name, daemon status, {@code ThreadGroup}, etc. + * + * @param r a runnable to be executed by new thread instance + * @return constructed thread, or {@code null} if the request to create a thread is rejected + */ + @Override + public Thread newThread(Runnable r) { + return new Thread(); + } + } +} + + + ]]> + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidUseTimerRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidUseTimerRule.xml new file mode 100644 index 0000000..1689692 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/AvoidUseTimerRule.xml @@ -0,0 +1,28 @@ + + + + AvoidUseTimer + 3 + 10,13,14 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/CountDownShouldInFinallyRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/CountDownShouldInFinallyRule.xml new file mode 100644 index 0000000..d592b0b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/CountDownShouldInFinallyRule.xml @@ -0,0 +1,88 @@ + + + + CountDownShouldInFinallyRule + 1 + 15 + + + + right + 0 + + + + right + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadLocalShouldRemoveRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadLocalShouldRemoveRule.xml new file mode 100644 index 0000000..cd199a0 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadLocalShouldRemoveRule.xml @@ -0,0 +1,85 @@ + + + + ThreadLocalShouldRemoveRule + 2 + 9,10 + local; + private static ThreadLocal local2; + + public void remove(){ + //local.remove(); + //local2.remove(); + } +} + + ]]> + + + + with remove + 0 + local; + private static ThreadLocal local2; + + public void remove(){ + local.remove(); + local2.remove(); + } +} + ]]> + + + + with initial 1 + 0 + df = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd"); + } + }; +} + + ]]> + + + + with initial 2 + 0 + df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); +} + + ]]> + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadPoolCreationRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadPoolCreationRule.xml new file mode 100644 index 0000000..38987ed --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadPoolCreationRule.xml @@ -0,0 +1,81 @@ + + + + Executors.new + 5 + 9,11,16,17,18 + System.out.println("xxx")); + java.util.concurrent.Executors.newSingleThreadExecutor().submit(() -> { + + }); + } +} + ]]> + + + no Executors + 0 + (), + Executors.defaultThreadFactory()); + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(); + } + }, (r, executor) -> { + + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + r -> { + Thread thread1 = new Thread(); + thread1.setName("xxx"); + return thread1; + }, new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + + } + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue()); + } +} + + ]]> + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadShouldSetNameRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadShouldSetNameRule.xml new file mode 100644 index 0000000..eb82c4c --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/concurrent/xml/ThreadShouldSetNameRule.xml @@ -0,0 +1,125 @@ + + + + ThreadShouldSetNameRule + 3 + 17,19,26 + () + , (r, executor) -> System.out.println("xxx")); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + + } + }); + new ThreadPoolExecutor(10,10,100,TimeUnit.MILLISECONDS,new SynchronousQueue<>(), + Executors.defaultThreadFactory()); + + } + + @Override + public Thread newThread(Runnable r) { + return null; + } +} + ]]> + + + right + 1 + 11 + + + + right + 0 + (),r -> { + Thread thread1 = new Thread(); + thread1.setName("xxx"); + return thread1; + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(),new ThreadNameTest()); + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(); + } + }, (r, executor) -> { + + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + r -> { + Thread thread1 = new Thread(); + thread1.setName("xxx"); + return thread1; + }, new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + + } + }); + new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new SynchronousQueue(), + r -> { + Thread thread1 = new Thread(); + thread1.setName("xxx"); + return thread1; + }); + } + + @Override + public Thread newThread(Runnable r) { + return null; + } +} + ]]> + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UndefineMagicConstantRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UndefineMagicConstantRule.xml new file mode 100644 index 0000000..6a0c84b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UndefineMagicConstantRule.xml @@ -0,0 +1,79 @@ + + + + + + + + UndefineMagicConstant. + 0 + + + + 2){ + } + if(i > 1){ + } + if(m > 1L){ + } + if(i != null){ + } + if(h != false){ + } + if(n.equals("")){ + } + for(int j=0 ; j< 10 ; i++){ + if(i > 2){ + + } + if(i != null){ + } + } + while(k < 1){ + if(i > 2){ + } + k++; + } + } + } + ]]> + + + + UndefineMagicConstant. + 2 + 8,20 + + + + + + + + UndefineMagicConstant. + 1 + 3 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UpperEllRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UpperEllRule.xml new file mode 100644 index 0000000..832b136 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/constant/xml/UpperEllRule.xml @@ -0,0 +1,40 @@ + + + + + + + + constants with uppercase L. + 0 + + + + + + + + constants with lowercase l. + 3 + 2,5,6 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/AvoidReturnInFinallyRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/AvoidReturnInFinallyRule.xml new file mode 100644 index 0000000..b428ec2 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/AvoidReturnInFinallyRule.xml @@ -0,0 +1,74 @@ + + + + + + + finally statement contains return + 1 + 6 + + + + + + + 1) { + return a; + } else { + return 0; + } + } + } + } + ]]> + + + finally statement has multiple return + 2 + 9,11 + + + + + + + + + + finally statement contains no return + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/MethodReturnWrapperTypeRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/MethodReturnWrapperTypeRule.xml new file mode 100644 index 0000000..ad1d839 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/MethodReturnWrapperTypeRule.xml @@ -0,0 +1,60 @@ + + + + + + + If the return type is primitive, return a value of wrapper class may cause NullPointerException + 8 + + 11,19,23,27,31,35,39,43 + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/TransactionMustHaveRollbackRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/TransactionMustHaveRollbackRule.xml new file mode 100644 index 0000000..29d5382 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/exception/xml/TransactionMustHaveRollbackRule.xml @@ -0,0 +1,154 @@ + + + + TransactionMustHaveRollbackRule + 1 + 17 + + + + + with rollback + 0 + + + + with rollback + 0 + + + + class + 1 + 10 + + + + + class with rollbackFor + 0 + + + + class with rollbackFor + 1 + 10 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/AvoidComplexConditionRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/AvoidComplexConditionRule.xml new file mode 100644 index 0000000..af5abf1 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/AvoidComplexConditionRule.xml @@ -0,0 +1,63 @@ + + + + 0)) { + return 1; + } + return 0; + } + + public int fn2(int a, int b, int c) { + return (a == 0 || b != 0 && c > 0) ? 1 : 0; + } + } + ]]> + + + complex conditional expression in if condition + 2 + 3,10 0 0 ˘ 0 /expected-linenumbers> + + + + + + + 0); + return flag ? 1 : 0; + } + } + ]]> + + + complex conditional expression extracted as variable + 0 + + + + + + + ids = new ArrayList<>(); + if (ids.size() > 0) { + + } + } + } + ]]> + + + simple conditional expression + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/NeedBraceRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/NeedBraceRule.xml new file mode 100644 index 0000000..1a4cc42 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/NeedBraceRule.xml @@ -0,0 +1,80 @@ + + + + 0) b++; + } + } + ]]> + + + if statement without {} + 1 + 5 + + + + + + + 0) b++; // BAD + + if (a > 0) { + b++; + } else if (a > -1) { + b++; + } else + b--; // BAD + + for (a = 0; a < 3; a ++) b--; // BAD + + while (a > 3) a--; // BAD + } + } +]]> + + + else,while,for statement without {} + 4 + 6,13,15,17 + + + + + + + 0) { + // do nothing + } + + else + + + flag = true; + } + } + ]]> + + + figure out else line no + 1 + + 12 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/SwitchStatementRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/SwitchStatementRule.xml new file mode 100644 index 0000000..6fb9e9c --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/flowcontrol/xml/SwitchStatementRule.xml @@ -0,0 +1,146 @@ + + + + + + + switch statement in outer class has no default block + 1 + 4 + + + + + + + + + + nested switch has no default block + 1 + 9 + + + + + + + + + + case statement without break + 1 + 4 + + + + + + + + + + default statement has no break + 0 + + + + + + + + + + + multiple continuous blank case + 1 + 4 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AbstractClassShouldStartWithAbstractNamingRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AbstractClassShouldStartWithAbstractNamingRule.xml new file mode 100644 index 0000000..7b5e2ae --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AbstractClassShouldStartWithAbstractNamingRule.xml @@ -0,0 +1,51 @@ + + + + + + abstract class name should start with Abstract or Base + 1 + + 1 + + + + + + + abstract class name should start with Abstract or Base + 0 + + + + + + + + abstract class name should start with Abstract or Base + 0 + + + + + + + + abstract class name should start with Abstract or Base + 1 + + 1 + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ArrayNamingShouldHaveBracketRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ArrayNamingShouldHaveBracketRule.xml new file mode 100644 index 0000000..96ec3a0 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ArrayNamingShouldHaveBracketRule.xml @@ -0,0 +1,36 @@ + + + + + + array should be array[] + 2 + + 2,6 + + + + + + + array should be array[] + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AvoidStartWithDollarAndUnderLineNamingRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AvoidStartWithDollarAndUnderLineNamingRule.xml new file mode 100644 index 0000000..86b7cd0 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/AvoidStartWithDollarAndUnderLineNamingRule.xml @@ -0,0 +1,32 @@ + + + + + + name should not be start with $ and _ + 4 + + 2,3,4,5 + + + + + + + name should not be start with $ and _ + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/BooleanPropertyShouldNotStartWithIsRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/BooleanPropertyShouldNotStartWithIsRule.xml new file mode 100644 index 0000000..c59b500 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/BooleanPropertyShouldNotStartWithIsRule.xml @@ -0,0 +1,51 @@ + + + + + + issuccess should not contain is + 2 + + 2,3 + + + + + + + + issuccess should not contain is + 0 + + + + + + + + issuccess should not contain is + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ClassNamingShouldBeCamelRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ClassNamingShouldBeCamelRule.xml new file mode 100644 index 0000000..651fa16 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ClassNamingShouldBeCamelRule.xml @@ -0,0 +1,105 @@ + + + + + + Class Name Should Camel + 6 + + 4,5,7,13,14,18 + + + + + + + + Class Name Should Camel1 + 0 + + + + + + + + Class Name Should Camel2 + 0 + + + + + + + + Class Name Should Camel3 + 0 + + + + + + + + Class Name Should Camel4 + 0 + + + + + + + + Class Name Should Camel5 + 0 + + + + + + + Class Name Should Camel6 + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ConstantFieldShouldBeUpperCaseRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ConstantFieldShouldBeUpperCaseRule.xml new file mode 100644 index 0000000..ed1d748 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ConstantFieldShouldBeUpperCaseRule.xml @@ -0,0 +1,45 @@ + + + + + + Constant name should be upper case + 2 + + 7,9 + + + + + + + Constant name should be upper case + 0 + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ExceptionClassShouldEndWithExceptionRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ExceptionClassShouldEndWithExceptionRule.xml new file mode 100644 index 0000000..3b39fde --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ExceptionClassShouldEndWithExceptionRule.xml @@ -0,0 +1,27 @@ + + + + + + Exception class name should end with Exception + 1 + + 1 + + + + + + + Exception class name should end with Exception + 0 + + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/LowerCamelCaseVariableNamingRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/LowerCamelCaseVariableNamingRule.xml new file mode 100644 index 0000000..80b711a --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/LowerCamelCaseVariableNamingRule.xml @@ -0,0 +1,129 @@ + + + + + + Variable name should be lowerCamelCase + 1 + + 3 + + + + + + + Variable name should be lowerCamelCase1 + 0 + + + + + + + + Variable name should be lowerCamelCase2 + 0 + + + + + + + + + + Variable name should be lowerCamelCase3 + 2 + + 2,3 + + + + + + + Variable name should be lowerCamelCase4 + 0 + + + + + + + + Variable name should be lowerCamelCase5 + 0 + + + + + + + + + Variable name should be lowerCamelCase6 + 0 + + + + + + + + Variable name should be lowerCamelCase7 + 0 + + + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/PackageNamingRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/PackageNamingRule.xml new file mode 100644 index 0000000..02ee353 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/PackageNamingRule.xml @@ -0,0 +1,35 @@ + + + + + + + Package Name should be lowercase + 1 + + 2 + + + + + + + + Package Name should be lowercase + 0 + + + + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ServiceOrDaoClassShouldEndWithImplRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ServiceOrDaoClassShouldEndWithImplRule.xml new file mode 100644 index 0000000..fa14bf1 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/ServiceOrDaoClassShouldEndWithImplRule.xml @@ -0,0 +1,53 @@ + + + + + + Service Class Name should be end with Impl + 2 + + 1,2 + + + + + + + + + Service Class Name should be end with Impl + 0 + + + + + + + + Service Class Name should be end with Impl + 0 + + + + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/TestClassShouldEndWithTestNamingRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/TestClassShouldEndWithTestNamingRule.xml new file mode 100644 index 0000000..1cf4f5c --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/naming/xml/TestClassShouldEndWithTestNamingRule.xml @@ -0,0 +1,35 @@ + + + + + + Test Class Name Should End With Test + + 0 + + + + + + + + Test Class Name Should End With Test + 1 + + 1 + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/EqualsAvoidNullRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/EqualsAvoidNullRule.xml new file mode 100644 index 0000000..3d1e959 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/EqualsAvoidNullRule.xml @@ -0,0 +1,132 @@ + + + + + + + + argument of equals is string literal + 3 + 6,10,14 + + + + + + + + + + argument of equals is constant + 1 + 8 + + + + + + + + + + non literal with equals + 0 + + + + + + + + + + multiple nested class + 2 + + + + + + + LOCAL_TEST_FLAG = new ThreadLocal(); + + public static boolean isLoadTestFlag() { + return Boolean.TRUE.equals(LOCAL_TEST_FLAG.get()); + } + } + ]]> + + + caller of equals is constant + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustOverrideToStringRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustOverrideToStringRule.xml new file mode 100644 index 0000000..174c2b4 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustOverrideToStringRule.xml @@ -0,0 +1,135 @@ + + + + + + + + POJO not override toString() + 2 + 1,6 + + + + + + + + + + POJO already override toString() + 0 + + + + + + + + + + lombok generated source not need override toString() + 0 + + + + + + + + + + lombok generated source not need override toString() + 0 + + + + + + + + + + import lombok.* + 0 + + + + + + + + + + use lombok.@ToString annotation + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustUsePrimitiveFieldRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustUsePrimitiveFieldRule.xml new file mode 100644 index 0000000..2637e2b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoMustUsePrimitiveFieldRule.xml @@ -0,0 +1,35 @@ + + + + + + + + POJO has primitive type fileds + 3 + 14,4,5 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoNoDefaultValueRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoNoDefaultValueRule.xml new file mode 100644 index 0000000..60fadbb --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/PojoNoDefaultValueRule.xml @@ -0,0 +1,90 @@ + + + + + + + + POJO has fields with default value + 3 + 12,3,5 + + + + + + + + + + non-POJO, inner POJO class + 1 + + + + + + + + + + + not check static, transient or public fields + 2 + 12,3 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/StringConcatRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/StringConcatRule.xml new file mode 100644 index 0000000..844b656 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/StringConcatRule.xml @@ -0,0 +1,178 @@ + + + + 0); + } + } + ]]> + + + concatenate strings with "+" in loop + 1 + 5 + + + + + + tags = new ArrayList(); + for (String tag : tags) { + stringBuilder.append(tag); + } + } + } + ]]> + + + concatenate strings with StringBuilder in loop + 0 + + + + + + tags = new ArrayList(); + for (String tag : tags) { + logger.info("tag=" + tag); + } + } + } + ]]> + + + concatenate strings with "+" in non-assignment statement + 0 + + + + + + tags = new ArrayList(); + for (String tag : tags) { + key = key + tag + "_SURFIX"; + } + } + } + ]]> + + + concatenate strings with "+" in loop, assigned value presents at right side of = + 1 + 9 + + + + ====================================================================== + + tags = new ArrayList(); + for (String tag : tags) { + String key = null; + key = key + tag + "_SURFIX"; + } + } + } + ]]> + + + concatenate strings with "+", assigned value defined in loop + 0 + + + + + + > convertRegister(Map> register) { + Map> newRegister = new HashMap>(); + for (Map.Entry> entry : register.entrySet()) { + String serviceName = entry.getKey(); + Map serviceUrls = entry.getValue(); + if (!serviceName.contains(":") && !serviceName.contains("/")) { + for (Map.Entry entry2 : serviceUrls.entrySet()) { + String serviceUrl = entry2.getKey(); + String serviceQuery = entry2.getValue(); + Map params = new HashMap<>(); + String group = params.get("group"); + String version = params.get("version"); + String name = serviceName; + if (group != null && group.length() > 0) { + name = group + "/" + name; + } + if (version != null && version.length() > 0) { + name = name + ":" + version; + } + Map newUrls = newRegister.get(name); + if (newUrls == null) { + newUrls = new HashMap(); + newRegister.put(name, newUrls); + } + newUrls.put(serviceUrl, params.toString()); + } + } else { + newRegister.put(serviceName, serviceUrls); + } + } + return newRegister; + } + } + ]]> + + + concatenate strings with "+" in nested loop + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/WrapperTypeEqualityRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/WrapperTypeEqualityRule.xml new file mode 100644 index 0000000..879830c --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/oop/xml/WrapperTypeEqualityRule.xml @@ -0,0 +1,151 @@ + + + + + + + + compare wrapper type objects without equals + 3 + 11,23,34 + + + + + + + importedExecutorsMethods = new HashSet<>(); + + @Override + public Object visit(ASTPrimaryExpression node, Object data) { + if (!executorsUsed && importedExecutorsMethods.isEmpty()) { + return super.visit(node, data); + } + + Token initToken = (Token) node.jjtGetFirstToken(); + if (!checkInitStatement(initToken)) { + addViolation(data, node); + } + return super.visit(node, data); + } + + private boolean checkInitStatement(Token token) { + String fullAssignStatement = getFullAssignStatement(token); + if (fullAssignStatement.startsWith(EXECUTORS_NEW)) { + return false; + } + if (!fullAssignStatement.startsWith(NEW) && !fullAssignStatement.startsWith(FULL_EXECUTORS_NEW)) { + return true; + } + // in case of lambda + int index = fullAssignStatement.indexOf(BRACKETS); + if (index == -1) { + return true; + } + fullAssignStatement = fullAssignStatement.substring(0, index); + + // avoid java.util.concurrent.Executors.newxxxx + if (importedExecutorsMethods.contains(fullAssignStatement)) { + return false; + } + // static import + return !importedExecutorsMethods.contains(Executors.class.getName() + DOT + fullAssignStatement); + } + + private String getFullAssignStatement(final Token token) { + if (token == null) { + return ""; + } + StringBuilder sb = new StringBuilder(48); + Token next = token; + while (next.next != null && !COLON.equals(next.image)) { + sb.append(next.image); + next = next.next; + } + return sb.toString(); + } + + @Override + public Object visit(ASTImportDeclaration node, Object data) { + ASTName name = node.getFirstChildOfType(ASTName.class); + // in case of static import + executorsUsed = executorsUsed || name.getType() == Executors.class; + if (name.getImage().startsWith(Executors.class.getName() + DOT)) { + importedExecutorsMethods.add(name.getImage()); + } + return super.visit(node, data); + } + } + ]]> + + + bugfix + 0 + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/orm/xml/IbatisMethodQueryForListRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/orm/xml/IbatisMethodQueryForListRule.xml new file mode 100644 index 0000000..692137e --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/orm/xml/IbatisMethodQueryForListRule.xml @@ -0,0 +1,26 @@ + + + + students = sqlMapClient.queryForList("student",1,10); + public List queryStudentByName(String name) { + List students = new SqlMapClient().queryForList("student",1,10); + return students; + } + public List queryStudentByName(String name) { + List teachers = sqlMapClient.queryForList("teacher",1,10); + return teachers; + } +} + ]]> + + + should not use ibatis method queryForList + 2 + + 4,10 + + diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidApacheBeanUtilsCopyRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidApacheBeanUtilsCopyRule.xml new file mode 100644 index 0000000..bd002e8 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidApacheBeanUtilsCopyRule.xml @@ -0,0 +1,39 @@ + + + + + + + + Use Apache BeanUtils copy + 1 + 5 + + + + + + + + Use Spring BeanUtils copy + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidMissUseOfMathRandomRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidMissUseOfMathRandomRule.xml new file mode 100644 index 0000000..4add692 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidMissUseOfMathRandomRule.xml @@ -0,0 +1,44 @@ + + + + + + + + Miss use of Math.random + 2 + 3,6 + + + + + + + + + Normal use of Math.random + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidNewDateGetTimeRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidNewDateGetTimeRule.xml new file mode 100644 index 0000000..e4ff31d --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidNewDateGetTimeRule.xml @@ -0,0 +1,37 @@ + + + + + + + + Use new Date().getTime() + 1 + 4 + + + + + + + + Not using newDate().getTime() + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidPatternCompileInMethodRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidPatternCompileInMethodRule.xml new file mode 100644 index 0000000..7c4377b --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/other/xml/AvoidPatternCompileInMethodRule.xml @@ -0,0 +1,54 @@ + + + + + + + + Use Pattern.compile as static field + 0 + + + + + + + + Use Pattern.compile in method string literal + 1 + 5 + + + + + + + + Use Pattern.compile in method not literal + 0 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithSubListToArrayListRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithSubListToArrayListRule.xml new file mode 100644 index 0000000..c0abbba --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithSubListToArrayListRule.xml @@ -0,0 +1,37 @@ + + + + list = new ArrayList(); + } + } + ]]> + + + + sets-ClassCastExceptionWithSubListToArrayListRule-ok. + 0 + + + + list = new ArrayList(); + list.add("22"); + List test = (ArrayList) list.subList(0, 1); + } + } + ]]> + + + + sets-ClassCastExceptionWithSubListToArrayListRule-warn. + 1 + 5 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithToArrayRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithToArrayRule.xml new file mode 100644 index 0000000..855fa17 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ClassCastExceptionWithToArrayRule.xml @@ -0,0 +1,35 @@ + + + + + + + + sets-ClassCastExceptionWithToArray-ok. + 0 + + + + + + + + sets-ClassCastExceptionWithToArray-warn. + 1 + 3 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/CollectionInitShouldAssignCapacityRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/CollectionInitShouldAssignCapacityRule.xml new file mode 100644 index 0000000..ef8cac5 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/CollectionInitShouldAssignCapacityRule.xml @@ -0,0 +1,39 @@ + + + + map = new HashMap(); + private void method(long aLong) { + Map map2 = new HashMap(16); + Map map3 = new ConcurrentHashMap(16); + List originList = new ArrayList(2048); + } + } + ]]> + + + + sets-CollectionInitShouldAssignCapacityRule-ok. + 0 + + + + map = new HashMap(); + Map map3 = new ConcurrentHashMap(); + } + } + ]]> + + + + sets-CollectionInitShouldAssignCapacityRule-warn. + 2 + 3,4 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ConcurrentExceptionWithModifyOriginSubListRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ConcurrentExceptionWithModifyOriginSubListRule.xml new file mode 100644 index 0000000..b444295 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/ConcurrentExceptionWithModifyOriginSubListRule.xml @@ -0,0 +1,42 @@ + + + + originList = new ArrayList(); + originList.add("22"); + List subList = originList.subList(0, 1); + } + } + ]]> + + + + sets-ConcurrentExceptionWithModifyOriginSubListRule-ok. + 0 + + + + originList = new ArrayList(); + originList.add("22"); + List subList = originList.subList(0, 1); + originList.add("22"); + originList.remove("22"); + originList.clear(); + } + } + ]]> + + + + sets-ConcurrentExceptionWithModifyOriginSubListRule-warn. + 3 + 6,7,8 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/DontModifyInForeachCircleRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/DontModifyInForeachCircleRule.xml new file mode 100644 index 0000000..2ebcf70 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/DontModifyInForeachCircleRule.xml @@ -0,0 +1,46 @@ + + + + originList = new ArrayList(); + originList.add("22"); + for (String item : originList) { + + } + originList.add("bb"); + } + } + ]]> + + + + sets-ConcurrentExceptionWithModifyOriginSubListRule-ok. + 0 + + + + originList = new ArrayList(); + originList.add("22"); + for (String item : originList) { + originList.add("bb"); + originList.remove("cc"); + originList.clear(); + } + } + } + ]]> + + + + sets-ConcurrentExceptionWithModifyOriginSubListRule-warn. + 3 + 6,7,8 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/UnsupportedExceptionWithModifyAsListRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/UnsupportedExceptionWithModifyAsListRule.xml new file mode 100644 index 0000000..4918700 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/java/rule/set/xml/UnsupportedExceptionWithModifyAsListRule.xml @@ -0,0 +1,52 @@ + + + + t = Arrays.asList("a","b","c"); + } + } + ]]> + + + + sets-UnsupportedExceptionWithModifyAsListRule-ok. + 0 + + + + list = Arrays.asList("a","b","c"); + list.add("d"); + list.remove("22"); + list.clear(); + } + List list = new ArrayList(); + list.add("b"); + list.remove("b"); + } + + private void method2() { + if(true){ + List list = Arrays.asList("a","b","c"); + list.add("d"); + } + List list = new ArrayList(); + list.add("b"); + } + } + ]]> + + + + sets-UnsupportedExceptionWithModifyAsListRule-warn. + 4 + 5,6,7,17 + + + + \ No newline at end of file diff --git a/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/vm/rule/other/xml/UseQuietReferenceNotationRule.xml b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/vm/rule/other/xml/UseQuietReferenceNotationRule.xml new file mode 100644 index 0000000..0f87e02 --- /dev/null +++ b/p3c-pmd/src/test/resources/com/alibaba/p3c/pmd/lang/vm/rule/other/xml/UseQuietReferenceNotationRule.xml @@ -0,0 +1,82 @@ + + + + + + + ]]> + + + + Use quiet reference notation + 0 + + + + + + + ]]> + + + + Not use quiet reference notation + 2 + 1,2 + + + + Velocity! +#end +
    +#foreach( $product in $allProducts ) +
  • $product
  • +#end +
+#include( "greetings.txt", $seasonalstock ) + ]]> +
+ + + SetDirectives is not considered + 1 + 8 + + + + + $something + #end +#end + ]]> + + + + Macro is not considered + 0 + + + + + + $!timezone.toLocal($currentDate,null,"Asia/Shanghai") + + ]]> + + + + Reference in parentheses is not considered + 0 + + +
\ No newline at end of file