Source code

pull/41/head
骏烈 7 years ago
parent f1e2331103
commit a375ac4796

95
.gitignore vendored

@ -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

@ -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的代码规约谈技术人成长的有所为”欢迎大家到场零距离沟通。
## <font color="green">Preface</font>
> 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.
电子工业出版社也将在一年后出版孤尽参与编写的“码出高效 - 阿里巴巴的Java开发手册详解”这本书。
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)*
代码规约最新更新消息关注“代码规约”微信公众号:
## <font color="green">Introduction</font>
The project consists of 3 parts:
- [PMD implementations](p3c-pmd)
- [IntelliJ IDEA plugin](idea-plugin)
- [Eclipse plugin](eclipse-plugin)
![代码规约](https://gw.alicdn.com/tfscom/TB1x5WJaGmgSKJjSsphXXcy1VXa.jpg)
## <font color="green">Rules</font>
<font color="blue">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:</font>
- ``[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.

@ -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

@ -0,0 +1,31 @@
# Eclipse Plugin
---
## <font color="green">Prepare</font>
- Eclipse Juno+
- maven3.+
- JDK 1.7+
## <font color="green">Build</font>
```
mvn -U clean install
```
## <font color="green">Install</font>
1. <font color="blue">Help >> Install New Software
then enter this update site URL [https://p3c.alibaba.com/plugin/eclipse/update](https://p3c.alibaba.com/plugin/eclipse/update)</font>
![Install Plugin](doc/images/install.png)
2. <font color="blue">Follow the wizard, restart Eclipse to take effect after install success.</font>
## <font color="green">Use</font>
1. <font color="blue">Switch language</font>
![Switch language](doc/images/eclipse_switch_language.png)
2. <font color="blue">Code Analyze </font>
![Analyze](doc/images/eclipse_analyze.png)
![Analyze](doc/images/analyze_result.png)

@ -0,0 +1,7 @@
bin.includes = feature.xml,\
feature.properties,\
smartfox.png
src.includes = build.properties,\
feature.properties,\
feature.xml,\
smartfox.png

@ -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\

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="com.alibaba.smartfox.eclipse.feature"
label="%feature.label"
version="1.0.0.qualifier"
provider-name="%feature.provider_name"
plugin="com.alibaba.smartfox.eclipse.plugin"
image="smartfox.png">
<description url="%description.url">
Alibaba Java Coding Guidelines
</description>
<copyright>
%copyright.text
</copyright>
<license url="%license.url">
%license.text
</license>
<url>
<update label="%feature.update_site_name" url="https://p3c.alibaba.com/plugin/eclipse/update"/>
</url>
<requires>
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.core.runtime"/>
<import plugin="org.eclipse.jdt.core"/>
<import plugin="org.eclipse.ui.ide"/>
<import plugin="org.eclipse.ui.views"/>
<import plugin="org.eclipse.core.resources"/>
<import plugin="org.eclipse.jface.text"/>
<import plugin="org.eclipse.ui.workbench.texteditor"/>
<import plugin="org.eclipse.ltk.core.refactoring"/>
<import plugin="org.eclipse.jdt.ui"/>
<import plugin="org.eclipse.core.filebuffers"/>
</requires>
<plugin
id="com.alibaba.smartfox.eclipse.plugin"
download-size="0"
install-size="0"
version="0.0.0"/>
</feature>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.smartfox.eclipse</groupId>
<artifactId>smartfox-eclipse</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.alibaba.smartfox.eclipse.feature</artifactId>
<packaging>eclipse-feature</packaging>
<inceptionYear>2017</inceptionYear>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -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

@ -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/

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
id="SmartFoxViews"
name="SmartFoxViews"
point="org.eclipse.ui.views">
<category
name="SmartFoxViews"
id="com.alibaba.smartfox.eclipse.ui">
</category>
<view
name="P3C Results"
allowMultiple="false"
icon="icons/view/smartfox_logo.png"
category="com.alibaba.smartfox.eclipse.ui"
class="com.alibaba.smartfox.eclipse.ui.InspectionResultView"
id="com.alibaba.smartfox.eclipse.ui.InspectionResultView">
</view>
<view
name="Rule Detail"
allowMultiple="false"
icon="icons/view/smartfox_logo.png"
category="com.alibaba.smartfox.eclipse.ui"
class="com.alibaba.smartfox.eclipse.ui.RuleDetailView"
id="com.alibaba.smartfox.eclipse.ui.RuleDetailView">
</view>
<stickyView
location="LEFT"
id="com.alibaba.smartfox.eclipse.ui.RuleDetailView">
</stickyView>
</extension>
<extension point="org.eclipse.core.expressions.definitions">
<definition id="when.alibaba.analysis.is.active">
<or>
<with variable="activePartId">
<equals
value="org.eclipse.jdt.ui.PackageExplorer">
</equals>
</with>
<with variable="activePartId">
<equals
value="org.eclipse.ui.navigator.ProjectExplorer">
</equals>
</with>
<with variable="activeSite">
<adapt
type="org.eclipse.ui.IEditorSite">
</adapt>
</with>
</or>
</definition>
</extension>
<extension
point="org.eclipse.ui.commands">
<category
description="Alibaba Code Analysis"
id="alibaba.ui.commands"
name="Alibaba Command">
</category>
<command
categoryId="alibaba.ui.commands"
description="Alibaba Code Analysis"
id="com.alibaba.smartfox.eclipse.command.analysis"
name="Alibaba Code Analysis"/>
<command
categoryId="alibaba.ui.commands"
description="Alibaba Code Analysis"
id="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler"
name="Switch Language"/>
</extension>
<extension
point="org.eclipse.ui.bindings">
<key
commandId="com.alibaba.smartfox.eclipse.command.analysis"
contextId="org.eclipse.ui.contexts.window"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="Ctrl+Shift+Alt+J">
</key>
</extension>
<extension
point="org.eclipse.ui.handlers">
<handler
class="com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler"
commandId="com.alibaba.smartfox.eclipse.command.analysis">
<enabledWhen>
<reference
definitionId="when.alibaba.analysis.is.active">
</reference>
</enabledWhen>
</handler>
<handler
class="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler"
commandId="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler">
</handler>
</extension>
<extension point="org.eclipse.ui.menus">
<menuContribution allPopups="false"
locationURI="popup:org.eclipse.ui.popup.any">
<command
commandId="com.alibaba.smartfox.eclipse.command.analysis"
icon="icons/ali-ide-run.png"
label="Alibaba Code Guidelines"
style="push">
<visibleWhen
checkEnabled="false">
<reference definitionId="when.alibaba.analysis.is.active"/>
</visibleWhen>
</command>
</menuContribution>
<menuContribution
allPopups="false"
locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions">
<toolbar
id="com.alibaba.smartfox.eclipse.plugin.toolbar3">
<command
commandId="com.alibaba.smartfox.eclipse.command.analysis"
icon="icons/ali-ide-run.png"
label="Alibaba Code Guidelines"
style="push"
tooltip="Alibaba Code Guidelines">
</command>
<command
commandId="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler"
icon="icons/language.png"
label="Switch Language"
style="push"
tooltip="Switch Language">
</command>
</toolbar>
</menuContribution>
</extension>
<extension
id="p3cMarker"
name="P3C Violations"
point="org.eclipse.core.resources.markers">
<persistent value="false"/>
<super type="org.eclipse.core.resources.problemmarker"/>
</extension>
<extension point="org.eclipse.ui.ide.markerResolution">
<markerResolutionGenerator
markerType="com.alibaba.smartfox.eclipse.plugin.p3cMarker"
class="com.alibaba.smartfox.eclipse.QuickFixGenerator"/>
</extension>
<extension point="org.eclipse.ui.preferencePages">
<page name="Alibaba Code Analysis"
class="com.alibaba.smartfox.eclipse.ui.AllRulesPreferencePage"
id="com.alibaba.smartfox.eclipse.ui.AllRulesPreferencePage"/>
</extension>
</plugin>

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.smartfox.eclipse</groupId>
<artifactId>smartfox-eclipse</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.alibaba.smartfox.eclipse.plugin</artifactId>
<packaging>eclipse-plugin</packaging>
<inceptionYear>2017</inceptionYear>
<properties>
<kotlin.compiler.incremental>false</kotlin.compiler.incremental>
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba.p3c</groupId>
<artifactId>p3c-pmd</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.7</version>
<configuration>
<stripVersion>true</stripVersion>
<prependGroupId>false</prependGroupId>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<!-- exclude the apex (transitive) dependencies - we use the shaded
version instead -->
<excludeGroupIds>p2.eclipse-plugin,apex</excludeGroupIds>
<excludeArtifactIds>com.alibaba.smartfox.eclipse.plugin
</excludeArtifactIds>
<useRepositoryLayout>false</useRepositoryLayout>
</configuration>
<executions>
<execution>
<id>get-dependencies</id>
<phase>process-sources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-dependency-plugin
</artifactId>
<versionRange>[0,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

@ -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<IMarkerResolution> {
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."
}
}

@ -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<String, Rule>
@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)
}
}
}

@ -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<Any?, Any?>?) {
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<IResource> {
val resources = mutableSetOf<IResource>()
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<IFile>()!!
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
}
}

@ -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<Any?, Any?>?) {
val text = P3cBundle.getMessage("$textKey${SmartfoxActivator.instance.locale}")
element.setText(text)
element.setTooltip(text)
}
}

@ -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<IResource>) {
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<MarkerViolation> {
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<RuleViolation> {
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()
}
}

@ -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
}
}

@ -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)
}
}

@ -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
}
}
}
}

@ -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
}
}

@ -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
}
}
}
}

@ -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
}
}
}
}

@ -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
}
}
}
}

@ -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())
}
}
}
}

@ -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
}
}

@ -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)
}
}

@ -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<Any> {
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<Any> {
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() {
}
}

@ -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()
}
}

@ -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<IMarker> {
val structured = treeViewer.selection as ITreeSelection
val elements = structured.iterator()
val result = HashSet<IMarker>()
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
}
}
}

@ -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<IFile, List<MarkerViolation>>()
var contentDescription = ""
val errors: List<LevelViolations> 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<MarkerViolation>): List<LevelViolations> {
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<MarkerViolation>) {
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<LevelViolations>): 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"
}
}

@ -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<FileMarkers>()
fun updateFileMarkers(markers: List<FileMarkers>) {
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
}
}

@ -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()
}
}

@ -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
}
}
}

@ -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<RuleViolations>,
var count: Int = rules.sumBy { it.count }) {
fun removeMarkers() {
rules.forEach {
it.removeMarkers()
}
}
}
data class RuleViolations(var rule: String, var files: List<FileMarkers>,
var count: Int = files.sumBy { it.markers.size }) {
fun removeMarkers() {
files.forEach {
it.removeMarkers()
}
}
}
data class FileMarkers(var file: IFile, var markers: List<MarkerViolation>) {
fun removeMarkers() {
MarkerUtil.removeAllMarkers(file)
}
}
data class MarkerViolation(val marker: IMarker, val violation: RuleViolation)

@ -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()
}
}

@ -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<IntArray>()
private val codeSpans = ArrayList<IntArray>()
private val linksBySpan = HashMap<IntArray, String>()
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<StyleRange>()
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<StyleRange>): Array<StyleRange> {
val styles = ranges.toTypedArray()
Arrays.sort(styles) { sr1, sr2 -> sr1.start - sr2.start }
return styles
}
}

@ -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)
}
}

@ -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<String> {
val lines = text.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (lines.isEmpty()) {
return emptyList()
}
val lineSet = ArrayList<String>(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'
}
}

@ -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<IntArray>
init {
commentOffsets = LinkedList<IntArray>()
}
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<StyleRange> {
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<StyleRange>()
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<StyleRange> {
val styles = ArrayList<StyleRange>()
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)
}
}

@ -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<String>? = 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<String>) {
this.keywords = keywords
}
fun setPunctuation(thePunctuationChars: String) {
punctuation = thePunctuationChars
}
}

@ -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<String, SyntaxData>()
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<String>()
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
}
}

@ -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<UndoEdit>()
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<UndoEdit>): 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<String, String>(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<ICleanUp>()
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<String, String>,
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<String, String>): 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<UndoEdit>,
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<UndoEdit>()
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<UndoEdit>,
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))
}
}

@ -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)

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler">阿里编码规约扫描</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.text.cur_zh">切换语言至英文(English)</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.text.cur_en">切换语言至中文</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.success.en">切换到English成功是否重启</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.success.zh">切换到中文成功,是否重启</entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.msg"><![CDATA[所有的覆写方法,必须加@Override注解。]]></entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.desc"><![CDATA[反例getObject()与get0bject()的问题。一个是字母的O一个是数字的0加@Override可以准确判断是否覆盖成功。另外如果在抽象类中对方法签名进行修改其实现类会马上编译报错。]]></entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.error"><![CDATA[方法缺少 '@Override' 注解]]></entry>
<entry key="rule.standalone.AvoidAccessStaticViaInstanceRule.msg"><![CDATA[避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。]]></entry>
<entry key="rule.standalone.AvoidUseDeprecationRule.msg"><![CDATA[不能使用过时的类或方法。]]></entry>
<entry key="rule.standalone.AvoidUseDeprecationRule.desc"><![CDATA[说明java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。]]></entry>
<entry key="rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.msg"><![CDATA[Map/Set的key为自定义对象时必须重写hashCode和equals。]]></entry>
<entry key="rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.desc"><![CDATA[关于hashCode和equals的处理遵循如下规则
1 只要重写equals就必须重写hashCode。
2 因为Set存储的是不重复的对象依据hashCode和equals进行判断所以Set存储的对象必须重写这两个方法。
3 如果自定义对象做为Map的键那么必须重写hashCode和equals。]]></entry>
</properties>

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler">Alibaba Coding Guidelines Analyze</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.text.cur_zh">Switch language to English</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.text.cur_en">Switch language to Chinese</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.success.en">Switch language to English successrestart?</entry>
<entry key="com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler.success.zh">Switch language to Chinese successrestart?</entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.msg"><![CDATA[An overridden method from an interface or abstract class must be marked with @Override annotation.]]></entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.desc"><![CDATA[
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.]]></entry>
<entry key="rule.standalone.MissingOverrideAnnotationRule.error"><![CDATA[Method missing '@Override' annotation]]></entry>
<entry key="rule.standalone.AvoidAccessStaticViaInstanceRule.msg"><![CDATA[A static field or method should be directly referred by its class name instead of its corresponding object name.]]></entry>
<entry key="rule.standalone.AvoidUseDeprecationRule.msg"><![CDATA[Using a deprecated class or method is prohibited.]]></entry>
<entry key="rule.standalone.AvoidUseDeprecationRule.desc"><![CDATA[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.]]></entry>
<entry key="rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.msg"><![CDATA[Custom class must override 'hashCode' and 'equals' while use as key for Map or value for Set.]]></entry>
<entry key="rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.desc"><![CDATA[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.]]></entry>
</properties>

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="alibaba-pmd"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<rule ref="rulesets/java/ali-concurrent.xml"/>
<rule ref="rulesets/java/ali-comment.xml"/>
<rule ref="rulesets/java/ali-naming.xml"/>
<rule ref="rulesets/java/ali-constant.xml"/>
<rule ref="rulesets/java/ali-other.xml"/>
<rule ref="rulesets/java/ali-flowcontrol.xml"/>
<rule ref="rulesets/java/ali-oop.xml"/>
<rule ref="rulesets/java/ali-orm.xml"/>
<rule ref="rulesets/java/ali-exception.xml"/>
<rule ref="rulesets/java/ali-set.xml"/>
<rule ref="rulesets/java/ali-ruleOnEclipse.xml"/>
</ruleset>

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="AlibabaRuleOnEclipse"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<rule name="MissingOverrideAnnotationRule" message="rule.standalone.MissingOverrideAnnotationRule.msg"
class="com.alibaba.smartfox.eclipse.pmd.rule.MissingOverrideAnnotationRule">
<description>rule.standalone.MissingOverrideAnnotationRule.desc</description>
<priority>1</priority>
<example>
<![CDATA[
/**
* @author caikang
* @date 2016/12/24
*/
public class MissingOverrideAnnotationRule extends AbstractEclipseRule {
@Override
public Object visit(ASTCompilationUnit node, Object data) {
return super.visit(node, data);
}
}
]]>
</example>
</rule>
<rule name="AvoidAccessStaticViaInstanceRule" message="rule.standalone.AvoidAccessStaticViaInstanceRule.msg"
class="com.alibaba.smartfox.eclipse.pmd.rule.AvoidAccessStaticViaInstanceRule">
<priority>1</priority>
</rule>
<rule name="AvoidUseDeprecationRule" message="rule.standalone.AvoidUseDeprecationRule.msg"
class="com.alibaba.smartfox.eclipse.pmd.rule.AvoidUseDeprecationRule">
<description>rule.standalone.AvoidUseDeprecationRule.desc</description>
<priority>2</priority>
</rule>
<rule name="MapOrSetKeyShouldOverrideHashCodeEqualsRule" message="rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.msg"
class="com.alibaba.smartfox.eclipse.pmd.rule.MapOrSetKeyShouldOverrideHashCodeEqualsRule">
<description>rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.desc</description>
<priority>1</priority>
</rule>
</ruleset>

@ -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

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<site>
<description url="https://p3c.alibaba.com/plugin/eclipse/update">
Alibaba Java Coding Guidelines
</description>
<feature id="com.alibaba.smartfox.eclipse.feature" version="0.0.0">
<category name="Smartfox"/>
</feature>
<category-def name="Smartfox" label="Smartfox Eclipse Plugin"/>
</site>

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.smartfox.eclipse</groupId>
<artifactId>smartfox-eclipse</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.alibaba.smartfox.eclipse.updatesite</artifactId>
<packaging>eclipse-repository</packaging>
<inceptionYear>2017</inceptionYear>
<profiles>
<profile>
<id>publish-to-update-site</id>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-p2-extras-plugin</artifactId>
<executions>
<execution>
<id>publish</id>
<phase>install</phase>
<goals>
<goal>mirror</goal>
</goals>
<configuration>
<source>
<repository>
<url>${project.build.directory}/repository</url>
</repository>
</source>
<append>true</append>
<destination>${eclipse.updatesite.path}</destination>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.smartfox.eclipse</groupId>
<artifactId>smartfox-eclipse</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<inceptionYear>2017</inceptionYear>
<properties>
<tycho.version>1.0.0</tycho.version>
<tycho-extras.version>${tycho.version}</tycho-extras.version>
<eclipse-repo.url>http://download.eclipse.org/releases/neon</eclipse-repo.url>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<ajdt-eclipse-repo.url>http://download.eclipse.org/tools/ajdt/46/dev/update</ajdt-eclipse-repo.url>
<kotlin.version>1.1.51</kotlin.version>
<eclipse-release>juno</eclipse-release>
</properties>
<modules>
<module>com.alibaba.smartfox.eclipse.plugin</module>
<module>com.alibaba.smartfox.eclipse.feature</module>
<module>com.alibaba.smartfox.eclipse.updatesite</module>
</modules>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--<repositories>
<repository>
<id>neon</id>
<url>${eclipse-repo.url}</url>
<layout>p2</layout>
</repository>
<repository>
<id>ajdt</id>
<url>${ajdt-eclipse-repo.url}</url>
<layout>p2</layout>
</repository>
</repositories>-->
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho.version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<configuration>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86</arch>
</environment>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
<environment>
<os>win32</os>
<ws>win32</ws>
<arch>x86</arch>
</environment>
<environment>
<os>win32</os>
<ws>win32</ws>
<arch>x86_64</arch>
</environment>
<environment>
<os>macosx</os>
<ws>cocoa</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<compilerVersion>1.7</compilerVersion>
<compilerArguments>
<inlineJSR/>
<enableJavadoc/>
<encoding>UTF-8</encoding>
</compilerArguments>
<compilerArgument>-err:-forbidden</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj.plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-packaging-plugin</artifactId>
<version>${tycho.version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-source-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<strictSrcIncludes>false</strictSrcIncludes>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-p2-director-plugin</artifactId>
<version>${tycho.version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-p2-repository-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<finalName>smartfox-eclipse-plugin</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-source-feature-plugin</artifactId>
<version>${tycho-extras.version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-custom-bundle-plugin</artifactId>
<version>${tycho-extras.version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-p2-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<baselineMode>warn</baselineMode>
<baselineReplace>none</baselineReplace>
<baselineRepositories>
<repository>
<url>http://download.eclipse.org/eclipse/updates/4.4</url>
</repository>
</baselineRepositories>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>macosx-jvm-flags</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<!-- tycho-surefire-plugin needs -XstartOnFirstThread on mac os -->
<os-jvm-flags>-XstartOnFirstThread</os-jvm-flags>
</properties>
</profile>
<!-- <profile>
<id>eclipse-indigo</id>
<activation>
<property>
<name>!eclipse-release</name>
</property>
</activation>
<repositories>
<repository>
<id>indigo</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/indigo/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://download.eclipse.org/technology/swtbot/releases/2.2.1/</url>
</repository>
</repositories>
</profile>
-->
<profile>
<id>eclipse-juno</id>
<activation>
<property>
<name>!eclipse-release</name>
</property>
</activation>
<repositories>
<repository>
<id>juno</id>
<layout>p2</layout>
<url>http://mirrors.ustc.edu.cn/eclipse/releases/juno/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://mirrors.ustc.edu.cn/eclipse/technology/swtbot/releases/2.5.0/</url>
</repository>
</repositories>
</profile>
<profile>
<id>eclipse-kepler</id>
<activation>
<property>
<name>eclipse-release</name>
<value>kepler</value>
</property>
</activation>
<repositories>
<repository>
<id>kepler</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/kepler/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://download.eclipse.org/technology/swtbot/releases/2.3.0/</url>
</repository>
</repositories>
</profile>
<profile>
<id>eclipse-luna</id>
<activation>
<property>
<name>eclipse-release</name>
<value>luna</value>
</property>
</activation>
<repositories>
<repository>
<id>luna</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/luna/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://download.eclipse.org/technology/swtbot/releases/2.3.0/</url>
</repository>
</repositories>
</profile>
<profile>
<id>eclipse-mars</id>
<activation>
<property>
<name>eclipse-release</name>
<value>mars</value>
</property>
</activation>
<repositories>
<repository>
<id>mars</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/mars/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://download.eclipse.org/technology/swtbot/releases/2.3.0/</url>
</repository>
</repositories>
</profile>
<profile>
<id>eclipse-neon</id>
<activation>
<property>
<name>eclipse-release</name>
<value>neon</value>
</property>
</activation>
<repositories>
<repository>
<id>neon</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/neon/</url>
</repository>
<repository>
<id>swtbot</id>
<layout>p2</layout>
<url>http://download.eclipse.org/technology/swtbot/releases/2.3.0/</url>
</repository>
</repositories>
</profile>
</profiles>
</project>

@ -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

@ -0,0 +1,62 @@
# Idea Plugin
---
## <font color="green">Prepare</font>
- Project JDK: 1.7+
- Gradle: 3.0+Require JDK1.8+ for gradle
## <font color="green">Build</font>
```
cd p3c-idea
gradle clean buildPlugin
```
## <font color="green">Run plugin</font>
```
cd p3c-idea
gradle runIde
# run specific IDEA
gradle runIde -Pidea_version=14.1.7
```
## <font color="green">Use p3c-common as your plugin dependency</font>
``` groovy
compile 'com.alibaba.p3c.idea:p3c-common:1.0.0'
```
## <font color="green">Install</font>
1. <font color="blue">Settings >> Plugins >> Browse repositories... </font>
![Switch language](doc/images/install_1.png)
2. <font color="blue"> Search plugin by keyword 'alibaba' then install 'Alibaba Java Coding Guidelines' plugin </font>
![Switch language](doc/images/install_2.png)
3. <font color="blue">Restart to take effect. </font>
## <font color="green">Use</font>
1. <font color="blue">Switch language</font>
![Switch language](doc/images/switch_language.png)
2. <font color="blue">Inspections</font>
![Real time](doc/images/inspection.png)
![Settings](doc/images/inspection_setting.png)
3. <font color="blue">Code Analyze</font>
![Settings](doc/images/analyze.png)
<font color="blue">We use the idea standard Inspection Results to show our violations.</font>
![Result](doc/images/inspection_result.png)
<font color="blue">We can also analyze file which is modified before vsc checkin.</font>
![Before Checkin](doc/images/analyze_before_checkin.png)

@ -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'
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

@ -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

Binary file not shown.

@ -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

172
idea-plugin/gradlew vendored

@ -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" "$@"

@ -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

@ -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
}

@ -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;
}

@ -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";
}

@ -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");
}

@ -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<PsiElement>(DataKeys.PSI_ELEMENT)
val psiFile = e.getData<PsiFile>(DataKeys.PSI_FILE)
val virtualFile = e.getData<VirtualFile>(DataKeys.VIRTUAL_FILE)
val virtualFiles = e.getData<Array<VirtualFile>>(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<VirtualFile>(*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<InspectionToolWrapper<*, *>>, 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<InspectionToolWrapper<*, *>>,
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)
}
}
}
}
}
}
}

@ -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}")
}
}

@ -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")
}
}
}

@ -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<InspectionToolWrapper<*, *>>,
managerEx: InspectionManagerEx, psiElement: PsiElement?): InspectionProfileImpl {
val profile = getProjectInspectionProfile(managerEx.project)
val allWrappers: LinkedHashSet<InspectionToolWrapper<*, *>> = 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<InspectionToolWrapper<*, *>>, 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
}
}

@ -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<InspectionToolWrapper<*, *>> {
val profile = InspectionProfileService.getProjectInspectionProfile(project)
return getAllTools(project, profile).filter(filter)
}
private fun getAllTools(project: Project, profile: InspectionProfileImpl): List<InspectionToolWrapper<*, *>> {
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<ScopeToolState>).map { it.tool }
}
}

@ -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)
}
}

@ -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"
}
}

@ -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<P3cConfig> {
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"
}
}

@ -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<SmartFoxProjectConfig> {
var inspectionProfileModifiedSet = Sets.newHashSet<String>()!!
var projectInspectionClosed = false
override fun getState(): SmartFoxProjectConfig? {
return this
}
override fun loadState(state: SmartFoxProjectConfig?) {
if (state == null) {
return
}
XmlSerializerUtil.copyBean(state, this)
}
}

@ -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<InspectionActionExtensionPoint>(
"${PluginVersions.pluginId.idString}.inspectionAction")!!
}
}

@ -0,0 +1,7 @@
/**
* extension point
*
* @author caikang
* @date 2017/06/19
*/
package com.alibaba.p3c.idea.ep;

@ -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()
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save