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
+
+
+
+ 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]]>
+
+
+
+避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
+
+