Repository: alibaba/p3c Branch: master Commit: 6c59c8c36ecd Files: 329 Total size: 1.1 MB Directory structure: gitextract_c6djlc1m/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── ------.md │ ├── bug_report.md │ ├── feature_request.md │ └── rule-issue-template.md ├── .gitignore ├── README.md ├── eclipse-plugin/ │ ├── .gitignore │ ├── README.md │ ├── README_cn.md │ ├── com.alibaba.smartfox.eclipse.feature/ │ │ ├── build.properties │ │ ├── feature.properties │ │ ├── feature.xml │ │ └── pom.xml │ ├── com.alibaba.smartfox.eclipse.plugin/ │ │ ├── META-INF/ │ │ │ └── MANIFEST.MF │ │ ├── build.properties │ │ ├── plugin.xml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── smartfox/ │ │ │ └── eclipse/ │ │ │ ├── QuickFix.kt │ │ │ ├── SmartfoxActivator.kt │ │ │ ├── handler/ │ │ │ │ ├── CodeAnalysisHandler.kt │ │ │ │ └── SwitchLanguageHandler.kt │ │ │ ├── job/ │ │ │ │ ├── CodeAnalysis.kt │ │ │ │ └── P3cMutex.kt │ │ │ ├── message/ │ │ │ │ └── P3cBundle.kt │ │ │ ├── pmd/ │ │ │ │ ├── RulePriority.kt │ │ │ │ └── rule/ │ │ │ │ ├── AbstractEclipseRule.kt │ │ │ │ ├── AvoidAccessStaticViaInstanceRule.kt │ │ │ │ ├── AvoidUseDeprecationRule.kt │ │ │ │ ├── MapOrSetKeyShouldOverrideHashCodeEqualsRule.kt │ │ │ │ └── MissingOverrideAnnotationRule.kt │ │ │ ├── ui/ │ │ │ │ ├── AllRulesPreferencePage.kt │ │ │ │ ├── AllRulesView.kt │ │ │ │ ├── InspectionResultTreeContentProvider.kt │ │ │ │ ├── InspectionResultTreeLabelProvider.kt │ │ │ │ ├── InspectionResultView.kt │ │ │ │ ├── InspectionResults.kt │ │ │ │ ├── QuickFixAction.kt │ │ │ │ ├── RuleDetailComposite.kt │ │ │ │ ├── RuleDetailView.kt │ │ │ │ ├── Violations.kt │ │ │ │ └── pmd/ │ │ │ │ ├── BasicLineStyleListener.kt │ │ │ │ ├── ContentBuilder.kt │ │ │ │ ├── FontBuilder.kt │ │ │ │ ├── StringArranger.kt │ │ │ │ ├── StyleExtractor.kt │ │ │ │ ├── SyntaxData.kt │ │ │ │ └── SyntaxManager.kt │ │ │ └── util/ │ │ │ ├── CleanUps.kt │ │ │ └── MarkerUtil.kt │ │ └── resources/ │ │ ├── messages/ │ │ │ ├── P3cBundle.xml │ │ │ └── P3cBundle_en.xml │ │ ├── rulesets/ │ │ │ └── java/ │ │ │ ├── ali-pmd.xml │ │ │ └── ali-ruleOnEclipse.xml │ │ └── syntax/ │ │ └── java.properties │ ├── com.alibaba.smartfox.eclipse.updatesite/ │ │ ├── category.xml │ │ └── pom.xml │ └── pom.xml ├── idea-plugin/ │ ├── .gitignore │ ├── README.md │ ├── README_cn.md │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── p3c-common/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── icons/ │ │ │ └── P3cIcons.java │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ ├── p3c/ │ │ │ │ └── idea/ │ │ │ │ ├── action/ │ │ │ │ │ ├── AliInspectionAction.kt │ │ │ │ │ ├── PmdGlobalInspectionContextImpl.kt │ │ │ │ │ ├── SwitchLanguageAction.kt │ │ │ │ │ └── ToggleProjectInspectionAction.kt │ │ │ │ ├── compatible/ │ │ │ │ │ └── inspection/ │ │ │ │ │ ├── InspectionProfileService.kt │ │ │ │ │ └── Inspections.kt │ │ │ │ ├── component/ │ │ │ │ │ ├── AliProjectComponent.kt │ │ │ │ │ └── CommonSettingsApplicationComponent.kt │ │ │ │ ├── config/ │ │ │ │ │ ├── P3cConfig.kt │ │ │ │ │ └── SmartFoxProjectConfig.kt │ │ │ │ ├── ep/ │ │ │ │ │ ├── InspectionActionExtensionPoint.kt │ │ │ │ │ └── package-info.java │ │ │ │ ├── i18n/ │ │ │ │ │ └── P3cBundle.kt │ │ │ │ ├── inspection/ │ │ │ │ │ ├── AliAccessToNonThreadSafeStaticFieldFromInstanceInspection.kt │ │ │ │ │ ├── AliArrayNamingShouldHaveBracketInspection.kt │ │ │ │ │ ├── AliBaseInspection.kt │ │ │ │ │ ├── AliControlFlowStatementWithoutBracesInspection.kt │ │ │ │ │ ├── AliEqualsAvoidNullInspection.kt │ │ │ │ │ ├── AliLocalInspectionToolProvider.kt │ │ │ │ │ ├── AliLongLiteralsEndingWithLowercaseLInspection.kt │ │ │ │ │ ├── AliPmdInspection.kt │ │ │ │ │ ├── AliPmdInspectionInvoker.kt │ │ │ │ │ ├── AliWrapperTypeEqualityInspection.kt │ │ │ │ │ ├── DelegateLocalInspectionTool.kt │ │ │ │ │ ├── DelegatePmdInspection.kt │ │ │ │ │ ├── PmdRuleInspectionIdentify.kt │ │ │ │ │ ├── RuleInspectionUtils.kt │ │ │ │ │ └── standalone/ │ │ │ │ │ ├── AliAccessStaticViaInstanceInspection.kt │ │ │ │ │ ├── AliDeprecationInspection.kt │ │ │ │ │ ├── AliMissingOverrideAnnotationInspection.kt │ │ │ │ │ └── MapOrSetKeyShouldOverrideHashCodeEqualsInspection.kt │ │ │ │ ├── pmd/ │ │ │ │ │ ├── AliPmdProcessor.kt │ │ │ │ │ ├── SourceCodeProcessor.kt │ │ │ │ │ └── index/ │ │ │ │ │ ├── InspectionDataSource.kt │ │ │ │ │ └── InspectionRenderer.kt │ │ │ │ ├── quickfix/ │ │ │ │ │ ├── AliQuickFix.kt │ │ │ │ │ ├── AvoidStartWithDollarAndUnderLineNamingQuickFix.kt │ │ │ │ │ ├── ClassMustHaveAuthorQuickFix.kt │ │ │ │ │ ├── ConstantFieldShouldBeUpperCaseQuickFix.kt │ │ │ │ │ ├── DecorateInspectionFix.kt │ │ │ │ │ ├── LowerCamelCaseVariableNamingQuickFix.kt │ │ │ │ │ └── VmQuietReferenceQuickFix.kt │ │ │ │ ├── util/ │ │ │ │ │ ├── DocumentUtils.kt │ │ │ │ │ ├── HighlightDisplayLevels.kt │ │ │ │ │ ├── HighlightInfoTypes.kt │ │ │ │ │ ├── HighlightSeverities.kt │ │ │ │ │ ├── NumberConstants.kt │ │ │ │ │ ├── ObjectConstants.kt │ │ │ │ │ ├── ProblemsUtils.kt │ │ │ │ │ ├── QuickFixes.kt │ │ │ │ │ └── withLockNotInline.kt │ │ │ │ └── vcs/ │ │ │ │ ├── AliCodeAnalysisCheckinHandler.kt │ │ │ │ └── AliCodeAnalysisCheckinHandlerFactory.kt │ │ │ └── smartfox/ │ │ │ └── idea/ │ │ │ └── common/ │ │ │ ├── component/ │ │ │ │ ├── AliBaseApplicationComponent.kt │ │ │ │ └── AliBaseProjectComponent.kt │ │ │ └── util/ │ │ │ ├── BalloonNotifications.kt │ │ │ ├── CommonExtensions.kt │ │ │ └── PluginVersions.kt │ │ └── resources/ │ │ ├── messages/ │ │ │ ├── P3cBundle.xml │ │ │ └── P3cBundle_en.xml │ │ ├── rulesets/ │ │ │ └── java/ │ │ │ └── ali-pmd.xml │ │ └── tpl/ │ │ └── StaticDescriptionTemplate.ftl │ ├── p3c-idea/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── resources/ │ │ └── META-INF/ │ │ ├── p3c.xml │ │ └── plugin.xml │ └── settings.gradle ├── license.txt ├── p3c-formatter/ │ ├── eclipse-codestyle.xml │ └── eclipse-codetemplate.xml ├── p3c-gitbook/ │ ├── .gitignore │ ├── MySQL数据库/ │ │ ├── ORM映射.md │ │ ├── SQL语句.md │ │ ├── 建表规约.md │ │ └── 索引规约.md │ ├── README.md │ ├── SUMMARY.md │ ├── book.json │ ├── styles/ │ │ └── website.css │ ├── 单元测试.md │ ├── 安全规约.md │ ├── 工程结构/ │ │ ├── 二方库依赖.md │ │ ├── 应用分层.md │ │ └── 服务器.md │ ├── 异常日志/ │ │ ├── 其他.md │ │ ├── 异常处理.md │ │ └── 日志规约.md │ ├── 本手册专有名词.md │ ├── 版本历史.md │ └── 编程规约/ │ ├── OOP规范.md │ ├── 代码格式.md │ ├── 命名风格.md │ ├── 常量定义.md │ ├── 并发处理.md │ ├── 控制语句.md │ ├── 注释规约.md │ └── 集合处理.md └── p3c-pmd/ ├── .gitignore ├── README.md ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── p3c/ │ │ └── pmd/ │ │ ├── I18nResources.java │ │ ├── fix/ │ │ │ └── FixClassTypeResolver.java │ │ └── lang/ │ │ ├── AbstractXpathRule.java │ │ ├── java/ │ │ │ ├── rule/ │ │ │ │ ├── AbstractAliRule.java │ │ │ │ ├── AbstractPojoRule.java │ │ │ │ ├── comment/ │ │ │ │ │ ├── AbstractAliCommentRule.java │ │ │ │ │ ├── AbstractMethodOrInterfaceMethodMustUseJavadocRule.java │ │ │ │ │ ├── AvoidCommentBehindStatementRule.java │ │ │ │ │ ├── ClassMustHaveAuthorRule.java │ │ │ │ │ ├── CommentsMustBeJavadocFormatRule.java │ │ │ │ │ ├── EnumConstantsMustHaveCommentRule.java │ │ │ │ │ └── RemoveCommentedCodeRule.java │ │ │ │ ├── concurrent/ │ │ │ │ │ ├── AvoidCallStaticSimpleDateFormatRule.java │ │ │ │ │ ├── AvoidConcurrentCompetitionRandomRule.java │ │ │ │ │ ├── AvoidManuallyCreateThreadRule.java │ │ │ │ │ ├── AvoidUseTimerRule.java │ │ │ │ │ ├── CountDownShouldInFinallyRule.java │ │ │ │ │ ├── ThreadLocalShouldRemoveRule.java │ │ │ │ │ ├── ThreadPoolCreationRule.java │ │ │ │ │ └── ThreadShouldSetNameRule.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── UndefineMagicConstantRule.java │ │ │ │ │ └── UpperEllRule.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── AvoidReturnInFinallyRule.java │ │ │ │ │ ├── MethodReturnWrapperTypeRule.java │ │ │ │ │ └── TransactionMustHaveRollbackRule.java │ │ │ │ ├── flowcontrol/ │ │ │ │ │ ├── AvoidComplexConditionRule.java │ │ │ │ │ ├── AvoidNegationOperatorRule.java │ │ │ │ │ ├── NeedBraceRule.java │ │ │ │ │ └── SwitchStatementRule.java │ │ │ │ ├── naming/ │ │ │ │ │ ├── AbstractClassShouldStartWithAbstractNamingRule.java │ │ │ │ │ ├── ArrayNamingShouldHaveBracketRule.java │ │ │ │ │ ├── AvoidStartWithDollarAndUnderLineNamingRule.java │ │ │ │ │ ├── BooleanPropertyShouldNotStartWithIsRule.java │ │ │ │ │ ├── ClassNamingShouldBeCamelRule.java │ │ │ │ │ ├── ConstantFieldShouldBeUpperCaseRule.java │ │ │ │ │ ├── ExceptionClassShouldEndWithExceptionRule.java │ │ │ │ │ ├── LowerCamelCaseVariableNamingRule.java │ │ │ │ │ ├── PackageNamingRule.java │ │ │ │ │ ├── ServiceOrDaoClassShouldEndWithImplRule.java │ │ │ │ │ └── TestClassShouldEndWithTestNamingRule.java │ │ │ │ ├── oop/ │ │ │ │ │ ├── BigDecimalAvoidDoubleConstructorRule.java │ │ │ │ │ ├── EqualsAvoidNullRule.java │ │ │ │ │ ├── PojoMustOverrideToStringRule.java │ │ │ │ │ ├── PojoMustUsePrimitiveFieldRule.java │ │ │ │ │ ├── PojoNoDefaultValueRule.java │ │ │ │ │ ├── StringConcatRule.java │ │ │ │ │ └── WrapperTypeEqualityRule.java │ │ │ │ ├── orm/ │ │ │ │ │ └── IbatisMethodQueryForListRule.java │ │ │ │ ├── other/ │ │ │ │ │ ├── AvoidApacheBeanUtilsCopyRule.java │ │ │ │ │ ├── AvoidDoubleOrFloatEqualCompareRule.java │ │ │ │ │ ├── AvoidMissUseOfMathRandomRule.java │ │ │ │ │ ├── AvoidNewDateGetTimeRule.java │ │ │ │ │ ├── AvoidPatternCompileInMethodRule.java │ │ │ │ │ ├── MethodTooLongRule.java │ │ │ │ │ └── UseRightCaseForDateFormatRule.java │ │ │ │ ├── set/ │ │ │ │ │ ├── ClassCastExceptionWithSubListToArrayListRule.java │ │ │ │ │ ├── ClassCastExceptionWithToArrayRule.java │ │ │ │ │ ├── CollectionInitShouldAssignCapacityRule.java │ │ │ │ │ ├── ConcurrentExceptionWithModifyOriginSubListRule.java │ │ │ │ │ ├── DontModifyInForeachCircleRule.java │ │ │ │ │ └── UnsupportedExceptionWithModifyAsListRule.java │ │ │ │ └── util/ │ │ │ │ ├── NodeSortUtils.java │ │ │ │ └── NodeUtils.java │ │ │ └── util/ │ │ │ ├── GeneratedCodeUtils.java │ │ │ ├── NumberConstants.java │ │ │ ├── PojoUtils.java │ │ │ ├── SpiLoader.java │ │ │ ├── StringAndCharConstants.java │ │ │ ├── VariableUtils.java │ │ │ ├── ViolationUtils.java │ │ │ └── namelist/ │ │ │ ├── NameListConfig.java │ │ │ ├── NameListService.java │ │ │ └── NameListServiceImpl.java │ │ └── vm/ │ │ └── rule/ │ │ └── other/ │ │ └── UseQuietReferenceNotationRule.java │ ├── kotlin/ │ │ └── com/ │ │ └── alibaba/ │ │ └── p3c/ │ │ └── pmd/ │ │ └── lang/ │ │ └── java/ │ │ └── rule/ │ │ └── concurrent/ │ │ └── LockShouldWithTryFinallyRule.kt │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ └── com.alibaba.p3c.pmd.lang.java.util.namelist.NameListService │ ├── messages.xml │ ├── messages_en.xml │ ├── namelist.properties │ └── rulesets/ │ ├── java/ │ │ ├── ali-comment.xml │ │ ├── ali-concurrent.xml │ │ ├── ali-constant.xml │ │ ├── ali-exception.xml │ │ ├── ali-flowcontrol.xml │ │ ├── ali-naming.xml │ │ ├── ali-oop.xml │ │ ├── ali-orm.xml │ │ ├── ali-other.xml │ │ └── ali-set.xml │ └── vm/ │ └── ali-other.xml └── test/ ├── java/ │ └── com/ │ └── alibaba/ │ └── p3c/ │ └── pmd/ │ ├── lang/ │ │ ├── java/ │ │ │ └── rule/ │ │ │ ├── comment/ │ │ │ │ └── CommentRulesTest.java │ │ │ ├── concurrent/ │ │ │ │ └── ConcurrentRuleTest.java │ │ │ ├── constant/ │ │ │ │ └── ConstantRulesTest.java │ │ │ ├── exception/ │ │ │ │ └── ExceptionRulesTest.java │ │ │ ├── flowcontrol/ │ │ │ │ └── FlowControlRuleTest.java │ │ │ ├── naming/ │ │ │ │ └── NamingRulesTest.java │ │ │ ├── oop/ │ │ │ │ └── OopRuleTest.java │ │ │ ├── orm/ │ │ │ │ └── OrmRulesTest.java │ │ │ ├── other/ │ │ │ │ ├── OtherRulesTest.java │ │ │ │ └── UseRightCaseForDateFormatRuleTest.java │ │ │ └── set/ │ │ │ └── SetRulesTest.java │ │ └── vm/ │ │ └── rule/ │ │ └── other/ │ │ └── OtherRulesTest.java │ └── testframework/ │ ├── ExtendRuleTst.java │ └── ExtendSimpleAggregatorTst.java └── resources/ └── com/ └── alibaba/ └── p3c/ └── pmd/ └── lang/ ├── java/ │ └── rule/ │ ├── comment/ │ │ └── xml/ │ │ ├── AbstractMethodOrInterfaceMethodMustUseJavadocRule.xml │ │ ├── AvoidCommentBehindStatementRule.xml │ │ ├── ClassMustHaveAuthorRule.xml │ │ ├── CommentsMustBeJavadocFormatRule.xml │ │ ├── EnumConstantsMustHaveCommentRule.xml │ │ └── RemoveCommentedCodeRule.xml │ ├── concurrent/ │ │ └── xml/ │ │ ├── AvoidCallStaticSimpleDateFormatRule.xml │ │ ├── AvoidConcurrentCompetitionRandomRule.xml │ │ ├── AvoidManuallyCreateThreadRule.xml │ │ ├── AvoidUseTimerRule.xml │ │ ├── CountDownShouldInFinallyRule.xml │ │ ├── LockShouldWithTryFinallyRule.xml │ │ ├── ThreadLocalShouldRemoveRule.xml │ │ ├── ThreadPoolCreationRule.xml │ │ └── ThreadShouldSetNameRule.xml │ ├── constant/ │ │ └── xml/ │ │ ├── UndefineMagicConstantRule.xml │ │ └── UpperEllRule.xml │ ├── exception/ │ │ └── xml/ │ │ ├── AvoidReturnInFinallyRule.xml │ │ ├── MethodReturnWrapperTypeRule.xml │ │ └── TransactionMustHaveRollbackRule.xml │ ├── flowcontrol/ │ │ └── xml/ │ │ ├── AvoidComplexConditionRule.xml │ │ ├── AvoidNegationOperatorRule.xml │ │ ├── NeedBraceRule.xml │ │ └── SwitchStatementRule.xml │ ├── naming/ │ │ └── xml/ │ │ ├── AbstractClassShouldStartWithAbstractNamingRule.xml │ │ ├── ArrayNamingShouldHaveBracketRule.xml │ │ ├── AvoidStartWithDollarAndUnderLineNamingRule.xml │ │ ├── BooleanPropertyShouldNotStartWithIsRule.xml │ │ ├── ClassNamingShouldBeCamelRule.xml │ │ ├── ConstantFieldShouldBeUpperCaseRule.xml │ │ ├── ExceptionClassShouldEndWithExceptionRule.xml │ │ ├── LowerCamelCaseVariableNamingRule.xml │ │ ├── PackageNamingRule.xml │ │ ├── ServiceOrDaoClassShouldEndWithImplRule.xml │ │ └── TestClassShouldEndWithTestNamingRule.xml │ ├── oop/ │ │ └── xml/ │ │ ├── BigDecimalAvoidDoubleConstructorRule.xml │ │ ├── EqualsAvoidNullRule.xml │ │ ├── PojoMustOverrideToStringRule.xml │ │ ├── PojoMustUsePrimitiveFieldRule.xml │ │ ├── PojoNoDefaultValueRule.xml │ │ ├── StringConcatRule.xml │ │ └── WrapperTypeEqualityRule.xml │ ├── orm/ │ │ └── xml/ │ │ └── IbatisMethodQueryForListRule.xml │ ├── other/ │ │ ├── java/ │ │ │ └── UseRightCaseForDateFormatRuleExam.java │ │ └── xml/ │ │ ├── AvoidApacheBeanUtilsCopyRule.xml │ │ ├── AvoidDoubleOrFloatEqualCompareRule.xml │ │ ├── AvoidMissUseOfMathRandomRule.xml │ │ ├── AvoidNewDateGetTimeRule.xml │ │ ├── AvoidPatternCompileInMethodRule.xml │ │ ├── MethodTooLongRule.xml │ │ └── UseRightCaseForDateFormatRule.xml │ └── set/ │ └── xml/ │ ├── ClassCastExceptionWithSubListToArrayListRule.xml │ ├── ClassCastExceptionWithToArrayRule.xml │ ├── CollectionInitShouldAssignCapacityRule.xml │ ├── ConcurrentExceptionWithModifyOriginSubListRule.xml │ ├── DontModifyInForeachCircleRule.xml │ └── UnsupportedExceptionWithModifyAsListRule.xml └── vm/ └── rule/ └── other/ └── xml/ └── UseQuietReferenceNotationRule.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/------.md ================================================ --- name: 规约问题模板 about: 规约问题请使用该模板 title: '' labels: '' assignees: '' --- ## 规约原文 ## 问题描述 ## 修改建议 ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/rule-issue-template.md ================================================ --- name: Rule issue template about: Rule issue please use this template. title: '' labels: '' assignees: '' --- ## Rule content ## Problem description ## Advice ================================================ FILE: .gitignore ================================================ # 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 ================================================ FILE: README.md ================================================ # P3C 最新版本:黄山版(2022.2.3发布) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) ## Preface > We are pleased to present Alibaba Java Coding Guidelines which consolidates the best programming practices over the years from Alibaba Group's technical teams. A vast number of Java programming teams impose demanding requirements on code quality across projects as we encourage reuse and better understanding of each other's programs. We have seen many programming problems in the past. For example, defective database table structures and index designs may cause software architecture flaws and performance risks. Another example is confusing code structures being difficult to maintain. Furthermore, vulnerable code without authentication is prone to hackers’ attacks. To address these kinds of problems, we developed this document for Java developers at Alibaba. For more information please refer the *Alibaba Java Coding Guidelines*: - 中文版: 直接下载上方的PDF文件(黄山版) - English Version: *[Alibaba Java Coding Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines)* ## Introduction The project consists of 3 parts: - [PMD implementations](p3c-pmd) - [IntelliJ IDEA plugin](idea-plugin) - [Eclipse plugin](eclipse-plugin) ## Rules Forty-nine rules are realized based on PMD, please refer the P3C-PMD documentation for more detailed information. Four rules are implemented within IDE plugins (IDEA and Eclipse) as follows: - ``[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. ================================================ FILE: eclipse-plugin/.gitignore ================================================ # Gradle build .gradle testdata/ # Java gitignore # .class .log # Package Files # *.war *.ear *.gradle #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 ================================================ FILE: eclipse-plugin/README.md ================================================ # Eclipse Plugin --- ## Prepare - Eclipse Juno+ - maven3.+ - JDK 1.7+ ## Build ``` mvn -U clean install ``` ## [中文使用手册](README_cn.md) ## Install 1. Help >> Install New Software then enter this update site URL [https://p3c.alibaba.com/plugin/eclipse/update](https://p3c.alibaba.com/plugin/eclipse/update) ![Install Plugin](doc/images/install.png) 2. Follow the wizard, restart Eclipse to take effect after install success. ## Use 1. Switch language ![Switch language](doc/images/eclipse_switch_language.png) 2. Code Analyze ![Analyze](doc/images/eclipse_analyze.png) ![Analyze](doc/images/analyze_result.png) ================================================ FILE: eclipse-plugin/README_cn.md ================================================ > 首先非常感谢大家对插件的支持与意见,Eclipse的功能相对来说比较简单,希望有更多的同学加入进来一起完善。 ## 插件安装 环境:JDK1.8,Eclipse4+。有同学遇到过这样的情况,安装插件重启后,发现没有对应的菜单项,从日志上也看不到相关的异常信息,最后把JDK从1.6升级到1.8解决问题。 Help -> Install New Software... ![](https://gw.alicdn.com/tfscom/TB1LOyPifJNTKJjSspoXXc6mpXa.png) 输入Update Site地址:https://p3c.alibaba.com/plugin/eclipse/update 回车,然后勾选Ali-CodeAnalysis,再一直点Next Next...按提示走下去就好。 然后就是提示重启了,安装完毕。 ![](https://gw.alicdn.com/tfscom/TB1Ud5kifBNTKJjSszcXXbO2VXa.png) 注意:有同学反映插件扫描会触发很多 "JPA Java Change Event Handler (Waiting)" 的任务,这个是Eclipse的一个[bug](https://bugs.eclipse.org/bugs/show_bug.cgi?id=387455),因为插件在扫描的时候会对文件进行标记,所以触发了JPA的任务。卸载JPA插件,或者尝试升级到最新版的Eclipse。附:[JPA project Change Event Handler问题解决](https://my.oschina.net/cimu/blog/278724) ## 插件使用 目前插件实现了开发手册中的53条规则,大部分基于PMD实现,其中有4条规则基于Eclipse实现,支持4条规则的QuickFix功能。 * 所有的覆写方法,必须加@Override注解, * if/for/while/switch/do等保留字与左右括号之间都必须加空格, * long或者Long初始赋值时,必须使用大写的L,不能是小写的l) * Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。 目前不支持代码实时检测,需要手动触发,希望更多的人加入进来一起把咱们的插件做得越来越好,尽量提升研发的使用体验。 ### 代码扫描 可以通过右键菜单、Toolbar按钮两种方式手动触发代码检测。同时结果面板中可以对部分实现了QuickFix功能的规则进行快速修复。 #### 触发扫描 在当前编辑的文件中点击右键,可以在弹出的菜单中触发对该文件的检测。 ![](https://gw.alicdn.com/tfscom/TB1XGo8iPihSKJjy0FeXXbJtpXa.png) 在左侧的Project目录树种点击右键,可以触发对整个工程或者选择的某个目录、文件进行检测。 ![](https://gw.alicdn.com/tfscom/TB18UsJi2NZWeJjSZFpXXXjBFXa.png) 也可以通过Toolbar中的按钮来触发检测,目前Toolbar的按钮触发的检测范围与您IDE当时的焦点有关,如当前编辑的文件或者是Project目录树选中的项,是不是感觉与右键菜单的检测范围类似呢。 ![](https://gw.alicdn.com/tfscom/TB1vt1oifBNTKJjSszcXXbO2VXa.png) #### 扫描结果 简洁的结果面板,按规则等级分类,等级->规则->文件->违规项。同时还提供一个查看规则详情的界面。 清除结果标记更方便,支持上面提到的4条规则QuickFix。 ![](https://gw.alicdn.com/tfscom/TB1_uFJi6ihSKJjy0FlXXadEXXa.png) #### 查看所有规则 ![](https://gw.alicdn.com/tfscom/TB1UNTnmYsTMeJjSszhXXcGCFXa.png) ![](https://gw.alicdn.com/tfscom/TB1_rf7sOAKL1JjSZFoXXagCFXa.png) #### 国际化 ![](https://gw.alicdn.com/tfscom/TB1KsyYsiFTMKJjSZFAXXckJpXa.png) ![](https://gw.alicdn.com/tfscom/TB19bzdm3oQMeJjy1XaXXcSsFXa.png) ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.feature/build.properties ================================================ bin.includes = feature.xml,\ feature.properties,\ smartfox.png src.includes = build.properties,\ feature.properties,\ feature.xml,\ smartfox.png ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.properties ================================================ 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\ ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.feature/feature.xml ================================================ Alibaba Java Coding Guidelines %copyright.text %license.text ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.feature/pom.xml ================================================ 4.0.0 com.alibaba.smartfox.eclipse smartfox-eclipse 2.0.1-SNAPSHOT com.alibaba.smartfox.eclipse.feature eclipse-feature 2017 ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/META-INF/MANIFEST.MF ================================================ 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: 2.0.1.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.8 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/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 ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/build.properties ================================================ 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/ ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/plugin.xml ================================================ ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/pom.xml ================================================ 4.0.0 com.alibaba.smartfox.eclipse smartfox-eclipse 2.0.1-SNAPSHOT com.alibaba.smartfox.eclipse.plugin eclipse-plugin 2017 false log4j log4j 1.2.17 com.alibaba.p3c p3c-pmd 2.0.1 org.jetbrains.kotlin kotlin-stdlib-jdk8 com.google.guava guava 20.0 src/main/kotlin org.jetbrains.kotlin kotlin-maven-plugin ${kotlin.version} compile compile compile test-compile test-compile test-compile org.apache.maven.plugins maven-dependency-plugin 3.1.1 true false ${project.build.directory}/lib p2.eclipse-plugin,apex com.alibaba.smartfox.eclipse.plugin false get-dependencies process-sources copy-dependencies org.eclipse.m2e lifecycle-mapping 1.0.0 org.apache.maven.plugins maven-dependency-plugin [0,) copy-dependencies ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/QuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse import com.alibaba.p3c.pmd.lang.java.rule.constant.UpperEllRule import com.alibaba.p3c.pmd.lang.java.rule.oop.EqualsAvoidNullRule import com.alibaba.smartfox.eclipse.ui.InspectionResults import com.alibaba.smartfox.eclipse.util.getRule import com.google.common.io.Files import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IMarker import org.eclipse.core.runtime.NullProgressMonitor import org.eclipse.ltk.core.refactoring.TextFileChange import org.eclipse.text.edits.ReplaceEdit import org.eclipse.ui.IMarkerResolution import org.eclipse.ui.IMarkerResolutionGenerator import java.nio.charset.Charset /** * * * @author caikang * @date 2017/06/14 */ class QuickFixGenerator : IMarkerResolutionGenerator { override fun getResolutions(marker: IMarker): Array { if (!marker.exists()) { return emptyArray() } val rule = marker.getRule() val quickFix = quickFixes[rule.name] ?: return emptyArray() return arrayOf(quickFix) } companion object { val quickFixes = mapOf(UpperEllRule::class.java.simpleName to UpperEllQuickFix, EqualsAvoidNullRule::class.java.simpleName to EqualsAvoidNullQuickFix) } } interface RunWithoutViewRefresh : IMarkerResolution { fun run(marker: IMarker, refresh: Boolean) override fun run(marker: IMarker) { run(marker, true) } } abstract class BaseQuickFix : RunWithoutViewRefresh { override fun run(marker: IMarker, refresh: Boolean) { if (!marker.exists()) { return } val file = marker.resource as IFile doRun(marker, file) marker.delete() if (refresh) { InspectionResults.removeMarker(marker) } } abstract fun doRun(marker: IMarker, file: IFile) } object UpperEllQuickFix : BaseQuickFix() { override fun doRun(marker: IMarker, file: IFile) { val offset = marker.getAttribute(IMarker.CHAR_START, 0) val end = marker.getAttribute(IMarker.CHAR_END, 0) val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) val replaceString = content.substring(offset, end + 1).replace("l", "L") val edit = ReplaceEdit(offset, replaceString.length, replaceString) val change = TextFileChange("", file) change.edit = edit change.perform(NullProgressMonitor()) } override fun getLabel(): String { return "Replace 'l' to 'L'." } } object EqualsAvoidNullQuickFix : BaseQuickFix() { val equalsName = ".equals(" override fun doRun(marker: IMarker, file: IFile) { val offset = marker.getAttribute(IMarker.CHAR_START, 0) val end = marker.getAttribute(IMarker.CHAR_END, 0) val content = Files.toString(file.rawLocation.toFile(), Charset.forName(file.charset)) val string = content.substring(offset, end) val list = string.split(equalsName).filterNotNull() if (list.size != 2) { return } val replace = "${list[1].substringBeforeLast(')')}$equalsName${list[0]})" val edit = ReplaceEdit(offset, string.length, replace) val change = TextFileChange("", file) change.edit = edit change.perform(NullProgressMonitor()) } override fun getLabel(): String { return "Flip equals." } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/SmartfoxActivator.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 import java.util.Locale /** * @author caikang * @date 2017/06/14 */ class SmartfoxActivator : AbstractUIPlugin() { init { aDefault = this } private val logger = Logger.getLogger(javaClass)!! lateinit var ruleSets: RuleSets private val localeKey = "p3c.locale" lateinit var ruleMap: Map @Throws(Exception::class) override fun start(context: BundleContext) { super.start(context) I18nResources.changeLanguage(locale) ruleSets = createRuleSets() ruleMap = ruleSets.allRules.associateBy { it.name } } @Throws(Exception::class) override fun stop(context: BundleContext?) { aDefault = null super.stop(context) } fun getImage(key: String, iconPath: String = key): Image { val registry = imageRegistry var image: Image? = registry.get(key) if (image == null) { val descriptor = getImageDescriptor(iconPath) registry.put(key, descriptor) image = registry.get(key) } return image!! } val locale: String get() { val language = preferenceStore.getString(localeKey) if (language.isNullOrBlank()) { val lang = Locale.getDefault().language return if (lang != Locale.ENGLISH.language && lang != Locale.CHINESE.language) { Locale.ENGLISH.language } else Locale.getDefault().language } return language } fun toggleLocale() { val lang = if (Locale.ENGLISH.language == locale) Locale.CHINESE.language else Locale.ENGLISH.language preferenceStore.setValue(localeKey, lang) } 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() return ruleSetFactory.createRuleSets("java-ali-pmd,vm-ali-other") } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/CodeAnalysisHandler.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.handler import com.alibaba.smartfox.eclipse.job.CodeAnalysis.processResources import com.alibaba.smartfox.eclipse.message.P3cBundle import com.google.common.collect.Sets import org.apache.log4j.Logger import org.eclipse.core.commands.AbstractHandler import org.eclipse.core.commands.ExecutionEvent import org.eclipse.core.commands.ExecutionException import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IResource import org.eclipse.core.resources.IResourceVisitor import org.eclipse.core.runtime.IAdaptable import org.eclipse.jface.viewers.IStructuredSelection import org.eclipse.ui.IFileEditorInput import org.eclipse.ui.IWorkingSet import org.eclipse.ui.commands.IElementUpdater import org.eclipse.ui.handlers.HandlerUtil import org.eclipse.ui.menus.UIElement import org.eclipse.ui.part.EditorPart import org.eclipse.ui.part.ViewPart /** * @author caikang * @date 2016/12/27 */ open class CodeAnalysisHandler : AbstractHandler(), IElementUpdater { override fun updateElement(element: UIElement, parameters: MutableMap?) { val text = P3cBundle.getMessage("com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler") element.setText(text) element.setTooltip(text) } @Throws(ExecutionException::class) override fun execute(executionEvent: ExecutionEvent): Any? { val selection = HandlerUtil.getCurrentSelectionChecked(executionEvent) val part = HandlerUtil.getActivePart(executionEvent) if (part is ViewPart) { if (selection is IStructuredSelection) { processForMutiFiles(selection) } } else if (part is EditorPart) { val editorInput = HandlerUtil.getActiveEditorInput(executionEvent) if (editorInput is IFileEditorInput) { processResources(setOf(editorInput.file)) } } return null } private fun processForMutiFiles(selection: IStructuredSelection) { val resources = getSelectionResources(selection) processResources(resources) } private fun getSelectionResources(selection: IStructuredSelection): MutableSet { val resources = mutableSetOf() selection.toList().forEach { when (it) { is IWorkingSet -> it.elements.mapTo(resources) { it.getAdapter(IResource::class.java) as IResource } is IAdaptable -> { val file = it.getAdapter(IResource::class.java) as? IResource ?: return@forEach resources.add(file) } else -> log.warn("The selected object is not adaptable : ${it.toString()}") } } return resources } companion object { private val log = Logger.getLogger(CodeAnalysisHandler::class.java) } } class FileCollectVisitor : IResourceVisitor { val fileSet = Sets.newLinkedHashSet()!! override fun visit(resource: IResource?): Boolean { if (resource == null) { return false } val file = resource.getAdapter(IFile::class.java) as? IFile ?: return true if (file.exists() && (file.fileExtension == "java" || file.fileExtension == "vm")) { fileSet.add(file) } return false } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/handler/SwitchLanguageHandler.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.handler import com.alibaba.smartfox.eclipse.SmartfoxActivator import com.alibaba.smartfox.eclipse.message.P3cBundle import org.eclipse.core.commands.AbstractHandler import org.eclipse.core.commands.ExecutionEvent import org.eclipse.jface.dialogs.MessageDialog import org.eclipse.ui.PlatformUI import org.eclipse.ui.commands.IElementUpdater import org.eclipse.ui.menus.UIElement /** * * * @author caikang * @date 2017/06/21 */ class SwitchLanguageHandler : AbstractHandler(), IElementUpdater { val handlerKey = "com.alibaba.smartfox.eclipse.handler.SwitchLanguageHandler" val textKey = "$handlerKey.text.cur_" override fun execute(executionEvent: ExecutionEvent): Any? { SmartfoxActivator.instance.toggleLocale() if (!MessageDialog.openConfirm(null, "Tips", P3cBundle.getMessage("$handlerKey.success.${SmartfoxActivator.instance.locale}"))) { return null } PlatformUI.getWorkbench().restart() return null } override fun updateElement(element: UIElement, parameters: MutableMap?) { val text = P3cBundle.getMessage("$textKey${SmartfoxActivator.instance.locale}") element.setText(text) element.setTooltip(text) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/CodeAnalysis.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.job import com.alibaba.p3c.pmd.lang.java.util.GeneratedCodeUtils import com.alibaba.smartfox.eclipse.SmartfoxActivator import com.alibaba.smartfox.eclipse.handler.CodeAnalysisHandler import com.alibaba.smartfox.eclipse.handler.FileCollectVisitor import com.alibaba.smartfox.eclipse.ui.InspectionResultView import com.alibaba.smartfox.eclipse.ui.InspectionResults import com.alibaba.smartfox.eclipse.ui.MarkerViolation import com.alibaba.smartfox.eclipse.util.MarkerUtil import com.google.common.io.Files import net.sourceforge.pmd.PMDConfiguration import net.sourceforge.pmd.PMDException import net.sourceforge.pmd.Report import net.sourceforge.pmd.RuleContext import net.sourceforge.pmd.RuleViolation import net.sourceforge.pmd.SourceCodeProcessor import org.apache.log4j.Logger import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IResource import org.eclipse.core.runtime.IProgressMonitor import org.eclipse.core.runtime.IStatus import org.eclipse.core.runtime.Status import org.eclipse.core.runtime.SubMonitor import org.eclipse.core.runtime.jobs.Job import java.io.IOException import java.io.StringReader import java.nio.charset.Charset /** * * * @author caikang * @date 2017/06/14 */ object CodeAnalysis { private val log = Logger.getLogger(CodeAnalysisHandler::class.java) fun processResources(resources: Set) { InspectionResultView.activeViews() val job = object : Job("P3C Code Analysis") { override fun run(monitor: IProgressMonitor): IStatus { val fileVisitor = FileCollectVisitor() monitor.setTaskName("Collect files") resources.forEach { if (monitor.isCanceled) { return@run Status.CANCEL_STATUS } if (it.isAccessible) { it.accept(fileVisitor) } } if (monitor.isCanceled) { return Status.CANCEL_STATUS } val subMonitor = SubMonitor.convert(monitor, "Analysis files", fileVisitor.fileSet.size) monitor.setTaskName("Analysis files") fileVisitor.fileSet.forEach { iFile -> if (monitor.isCanceled) { return@run Status.CANCEL_STATUS } MarkerUtil.removeAllMarkers(iFile) monitor.subTask(iFile.fullPath.toPortableString()) val markers = processFileToMakers(iFile, monitor) subMonitor.newChild(1) InspectionResults.updateFileViolations(iFile, markers) } return Status.OK_STATUS } } job.apply { isUser = true isSystem = false priority = Job.INTERACTIVE rule = P3cMutex schedule() } } fun processFileToMakers(file: IFile, monitor: IProgressMonitor): List { file.refreshLocal(IResource.DEPTH_ZERO, monitor) val ruleViolations = processFile(file) MarkerUtil.removeAllMarkers(file) return ruleViolations.map { MarkerViolation(MarkerUtil.addMarker(file, it), it) } } private fun processFile(file: IFile): List { val configuration = PMDConfiguration() configuration.setSourceEncoding(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, 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() } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/job/P3cMutex.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.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 } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/message/P3cBundle.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/RulePriority.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AbstractEclipseRule.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidAccessStaticViaInstanceRule.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { val parent = node!!.parent if (parent !is MethodInvocation && 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) { when (parent) { is MethodInvocation -> { val methodBinding = parent.resolveMethodBinding() val methodTypeBinding = methodBinding.declaringClass if (methodBinding.modifiers and Modifier.STATIC != 0 && qualifier.fullyQualifiedName != methodTypeBinding.name && methodTypeBinding == typeBinding && binding.name != typeBinding.name) { violation(ruleContext, parent, ast) return false } } else -> { } } if (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 } } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/AvoidUseDeprecationRule.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MapOrSetKeyShouldOverrideHashCodeEqualsRule.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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" private val skipJdkPackageJava = "java." private val skipJdkPackageJavax = "javax." 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 { val skip = genericType.isEnum || genericType.isInterface || genericType.isArray || genericType.isTypeVariable || genericType.isWildcardType || genericType.qualifiedName?.startsWith(skipJdkPackageJava) ?: false || genericType.qualifiedName?.startsWith(skipJdkPackageJavax) ?: false // skip if (skip) { 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 } } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/pmd/rule/MissingOverrideAnnotationRule.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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()) } } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesPreferencePage.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/AllRulesView.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeContentProvider.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.ui import org.eclipse.jface.viewers.ITreeContentProvider import org.eclipse.jface.viewers.Viewer /** * * * @author caikang * @date 2017/06/08 */ object InspectionResultTreeContentProvider : ITreeContentProvider { private lateinit var input: InspectionResults override fun getParent(element: Any?): Any { return input } override fun hasChildren(element: Any?): Boolean { return element is InspectionResults || element is LevelViolations || element is RuleViolations || element is FileMarkers } override fun getChildren(parentElement: Any?): Array { if (parentElement is InspectionResults) { return parentElement.errors.toTypedArray() } if (parentElement is LevelViolations) { return parentElement.rules.toTypedArray() } if (parentElement is RuleViolations) { return parentElement.files.toTypedArray() } if (parentElement is FileMarkers) { return parentElement.markers.toTypedArray() } return emptyArray() } override fun getElements(inputElement: Any?): Array { return input.errors.toTypedArray() } override fun inputChanged(viewer: Viewer?, oldInput: Any?, newInput: Any?) { if (newInput == null) { return } this.input = newInput as InspectionResults } override fun dispose() { } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultTreeLabelProvider.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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() } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResultView.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.ui import com.alibaba.smartfox.eclipse.SmartfoxActivator import com.alibaba.smartfox.eclipse.job.P3cMutex import com.alibaba.smartfox.eclipse.util.MarkerUtil import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IMarker import org.eclipse.core.runtime.IProgressMonitor import org.eclipse.core.runtime.IStatus import org.eclipse.core.runtime.Status import org.eclipse.core.runtime.jobs.Job import org.eclipse.jface.action.Action import org.eclipse.jface.action.Separator import org.eclipse.jface.util.OpenStrategy import org.eclipse.jface.viewers.ISelection import org.eclipse.jface.viewers.IStructuredSelection import org.eclipse.jface.viewers.ITreeSelection import org.eclipse.jface.viewers.TreeViewer import org.eclipse.swt.SWT import org.eclipse.swt.layout.FillLayout import org.eclipse.swt.widgets.Composite import org.eclipse.swt.widgets.Display import org.eclipse.ui.IWorkbenchPage import org.eclipse.ui.OpenAndLinkWithEditorHelper import org.eclipse.ui.PartInitException import org.eclipse.ui.PlatformUI import org.eclipse.ui.ide.IDE import org.eclipse.ui.ide.ResourceUtil import org.eclipse.ui.internal.views.markers.MarkerSupportInternalUtilities import org.eclipse.ui.part.ViewPart import org.eclipse.ui.texteditor.ITextEditor import java.util.HashSet /** * * * @author caikang * @date 2017/06/08 */ class InspectionResultView : ViewPart() { lateinit var treeViewer: TreeViewer private val quickFixAction = QuickFixAction(this) override fun setFocus() { treeViewer.control.setFocus() } override fun createPartControl(parent: Composite) { parent.layout = FillLayout() treeViewer = TreeViewer(parent, SWT.MULTI or SWT.H_SCROLL or SWT.V_SCROLL) treeViewer.setUseHashlookup(true) treeViewer.contentProvider = InspectionResultTreeContentProvider treeViewer.labelProvider = InspectionResultTreeLabelProvider treeViewer.input = InspectionResults InspectionResults.view = this addDoubleClickListener() addSelectionChangedListener() initToolBar() addLinkWithEditorSupport() site.selectionProvider = treeViewer } fun clear() { InspectionResults.clear() refreshView(InspectionResults) } fun refreshView(input: InspectionResults) { Display.getDefault().asyncExec { treeViewer.refresh(input, true) contentDescription = input.contentDescription } } private fun addSelectionChangedListener() { treeViewer.addSelectionChangedListener inner@ { val selection = it.selection as? IStructuredSelection ?: return@inner val item = selection.firstElement ?: return@inner val ruleDetailView = RuleDetailView.showAndGetView() when (item) { is MarkerViolation -> { ruleDetailView.refresh(item.violation.rule) quickFixAction.updateFileMarkers(listOf(FileMarkers(item.marker.resource as IFile, listOf(item)))) } is FileMarkers -> { ruleDetailView.refresh(item.markers.first().violation.rule) quickFixAction.updateFileMarkers(listOf(item)) } is RuleViolations -> { ruleDetailView.refresh(SmartfoxActivator.instance.getRule(item.rule)) quickFixAction.updateFileMarkers(item.files) } else -> { quickFixAction.updateFileMarkers(emptyList()) } } } } private fun initToolBar() { val bars = viewSite.actionBars val tm = bars.toolBarManager val clearAction = object : Action("Clear Markers") { override fun run() { val job = object : Job("Clear Markers") { override fun run(monitor: IProgressMonitor): IStatus { if (monitor.isCanceled) { Status.CANCEL_STATUS } clear() return Status.OK_STATUS } } job.rule = P3cMutex job.schedule() } } clearAction.imageDescriptor = SmartfoxActivator.getImageDescriptor("icons/actions/clear.png") tm.add(Separator("Markers")) tm.add(clearAction) tm.add(Separator("FilterGroup")) tm.add(quickFixAction) } private fun addLinkWithEditorSupport() { object : OpenAndLinkWithEditorHelper(treeViewer) { override fun activate(selection: ISelection) { val currentMode = OpenStrategy.getOpenMethod() try { OpenStrategy.setOpenMethod(OpenStrategy.DOUBLE_CLICK) openSelectedMarkers() } finally { OpenStrategy.setOpenMethod(currentMode) } } override fun linkToEditor(selection: ISelection?) { } override fun open(selection: ISelection, activate: Boolean) { val structured = selection as ITreeSelection val element = structured.firstElement as? MarkerViolation ?: return val page = site.page if (element.marker.exists()) { openMarkerInEditor(element.marker, page) return } val file = element.marker.resource as IFile val editor = IDE.openEditor(page, file) as ITextEditor editor.selectAndReveal(MarkerUtil.getAbsoluteRange(file, element.violation).start, 0) } } } internal fun openSelectedMarkers() { val markers = getOpenableMarkers() for (marker in markers) { val page = site.page openMarkerInEditor(marker, page) } } private fun getOpenableMarkers(): Array { val structured = treeViewer.selection as ITreeSelection val elements = structured.iterator() val result = HashSet() while (elements.hasNext()) { val marker = elements.next() as? IMarker ?: return emptyArray() result.add(marker) } return result.toTypedArray() } fun openMarkerInEditor(marker: IMarker?, page: IWorkbenchPage) { val editor = page.activeEditor if (editor != null) { val input = editor.editorInput val file = ResourceUtil.getFile(input) if (file != null) { if (marker!!.resource == file && OpenStrategy.activateOnOpen()) { page.activate(editor) } } } if (marker != null && marker.resource is IFile) { try { IDE.openEditor(page, marker, OpenStrategy.activateOnOpen()) } catch (e: PartInitException) { MarkerSupportInternalUtilities.showViewError(e) } } } private fun addDoubleClickListener() { treeViewer.addDoubleClickListener({ event -> val selection = event.selection if (selection !is ITreeSelection || selection.size() != 1) { return@addDoubleClickListener } val obj = selection.firstElement if (treeViewer.isExpandable(obj)) { treeViewer.setExpandedState(obj, !treeViewer.getExpandedState(obj)) } }) } companion object { val viewId = "com.alibaba.smartfox.eclipse.ui.InspectionResultView" fun activeViews() { PlatformUI.getWorkbench().activeWorkbenchWindow.activePage.showView(viewId) RuleDetailView.showAndGetView() } fun getView(): InspectionResultView { return PlatformUI.getWorkbench().activeWorkbenchWindow.activePage.findView(viewId) as InspectionResultView } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/InspectionResults.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { private val fileViolations = linkedMapOf>() var contentDescription = "" val errors: List get() { val result = toLevelViolationList(fileViolations.values.flatten()) contentDescription = getContentDescription(result) return result } lateinit var view: InspectionResultView fun clear() { fileViolations.forEach { MarkerUtil.removeAllMarkers(it.key) } fileViolations.clear() // update contentDescription errors } private fun toLevelViolationList(markers: Collection): List { return markers.groupBy { it.violation.rule.priority.priority }.mapValues { it.value.groupBy { it.violation.rule.name }.mapValues { it.value.groupBy { it.marker.resource as IFile }.map { FileMarkers(it.key, it.value) } }.map { RuleViolations(it.key, it.value) } }.toSortedMap().map { val level = RulePriority.valueOf(it.key).title LevelViolations(level, it.value) } } fun updateFileViolations(file: IFile, markers: List) { if (markers.isEmpty()) { fileViolations.remove(file) } else { fileViolations[file] = markers } view.refreshView(this) } fun removeMarker(marker: IMarker) { val file = marker.resource as IFile val list = fileViolations[file] ?: return val result = list.filter { it.marker != marker } fileViolations[file] = result marker.delete() view.refreshView(this) } fun getContentDescription(errors: List): String { val map = errors.associateBy { it.level } return "${map[RulePriority.Blocker.title]?.count ?: 0} Blockers," + "${map[RulePriority.Critical.title]?.count ?: 0} Criticals," + "${map[RulePriority.Major.title]?.count ?: 0} Majors" } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/QuickFixAction.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.ui import com.alibaba.p3c.pmd.lang.java.rule.flowcontrol.NeedBraceRule import com.alibaba.smartfox.eclipse.RunWithoutViewRefresh import com.alibaba.smartfox.eclipse.SmartfoxActivator import com.alibaba.smartfox.eclipse.job.CodeAnalysis import com.alibaba.smartfox.eclipse.job.P3cMutex import com.alibaba.smartfox.eclipse.pmd.rule.MissingOverrideAnnotationRule import com.alibaba.smartfox.eclipse.util.CleanUps import com.alibaba.smartfox.eclipse.util.getResolution import org.eclipse.core.runtime.IProgressMonitor import org.eclipse.core.runtime.IStatus import org.eclipse.core.runtime.Status import org.eclipse.core.runtime.SubMonitor import org.eclipse.core.runtime.jobs.Job import org.eclipse.jface.action.Action /** * * * @author caikang * @date 2017/06/14 */ class QuickFixAction(val view: InspectionResultView) : Action("Quick Fix") { init { imageDescriptor = SmartfoxActivator.getImageDescriptor("icons/actions/quickfixBulb.png") isEnabled = false } var markers = listOf() fun updateFileMarkers(markers: List) { this.markers = markers isEnabled = enabled() } override fun run() { if (markers.isEmpty()) { return } runJob() } private fun runJob() { val job = object : Job("Perform P3C 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 } } val outJob = object:Job("P3C Quick Fix Wait analysis finish"){ override fun run(monitor: IProgressMonitor?): IStatus { job.schedule() return Status.OK_STATUS } } outJob.rule = P3cMutex outJob.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 } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailComposite.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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() } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/RuleDetailView.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/Violations.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.eclipse.ui import com.alibaba.smartfox.eclipse.util.MarkerUtil import net.sourceforge.pmd.RuleViolation import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IMarker /** * * * @author caikang * @date 2017/06/13 */ data class LevelViolations(var level: String, var rules: List, var count: Int = rules.sumBy { it.count }) { fun removeMarkers() { rules.forEach { it.removeMarkers() } } override fun equals(other: Any?): Boolean { if (other !is LevelViolations) { return false } return level == other.level } override fun hashCode(): Int { return level.hashCode() } } data class RuleViolations(var rule: String, var files: List, var count: Int = files.sumBy { it.markers.size }) { fun removeMarkers() { files.forEach { it.removeMarkers() } } override fun equals(other: Any?): Boolean { if (other !is RuleViolations) { return false } return rule == other.rule } override fun hashCode(): Int { return rule.hashCode() } } data class FileMarkers(var file: IFile, var markers: List) { fun removeMarkers() { MarkerUtil.removeAllMarkers(file) } override fun equals(other: Any?): Boolean { if (other !is FileMarkers) { return false } return file == other.file } override fun hashCode(): Int { return file.hashCode() } } data class MarkerViolation(val marker: IMarker, val violation: RuleViolation) ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/BasicLineStyleListener.kt ================================================ 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() } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/ContentBuilder.kt ================================================ package com.alibaba.smartfox.eclipse.ui.pmd import net.sourceforge.pmd.lang.Language import org.eclipse.swt.SWT import org.eclipse.swt.custom.StyleRange import org.eclipse.swt.custom.StyledText import org.eclipse.swt.graphics.Point import org.eclipse.swt.widgets.Display import org.eclipse.ui.PlatformUI import java.net.URL import java.util.ArrayList import java.util.Arrays import java.util.HashMap /** * * @author caikang * @date 2017/07/20 */ class ContentBuilder { private val CR = '\n' private val buffer = StringBuilder() private val headingSpans = ArrayList() private val codeSpans = ArrayList() private val linksBySpan = HashMap() private val indentDepth: Int = 3 private val codeStyleExtractor = StyleExtractor(SyntaxManager.getSyntaxData("java")) private val background = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE) private val headingColor = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE) private val codeStyle = FontBuilder("Courier", 11, SWT.NORMAL).style(Display.getCurrent()) fun clear() { buffer.setLength(0) headingSpans.clear() codeSpans.clear() linksBySpan.clear() } fun addHeading(heading: String) { var length = buffer.length if (length > 0) { buffer.append(CR) length += 1 } headingSpans.add(intArrayOf(length, length + heading.length)) buffer.append(heading).append(CR) } fun addText(text: String) { for (i in 0 until indentDepth) { buffer.append(' ') } buffer.append(text).append(CR) } fun addRawText(text: String) { buffer.append(text) } fun addCode(code: String) { val length = buffer.length codeSpans.add(intArrayOf(length, length + code.length)) buffer.append(code) } fun setLanguage(language: Language) { val syntax = SyntaxManager.getSyntaxData(language.terseName) codeStyleExtractor.syntax(syntax) } fun hasLinks(): Boolean { return linksBySpan.isNotEmpty() } fun addLinkHandler(widget: StyledText) { widget.addListener(SWT.MouseDown) { event -> // It is up to the application to determine when and how a link // should be activated. // In this snippet links are activated on mouse down when the // control key is held down // if ((event.stateMask & SWT.MOD1) != 0) { try { val offset = widget.getOffsetAtLocation(Point(event.x, event.y)) val link = linkAt(offset) if (link != null) { launchBrowser(link) } } catch (e: IllegalArgumentException) { // no character under event.x, event.y } } } private fun linkAt(textIndex: Int): String? { var span: IntArray for ((key, value) in linksBySpan) { span = key if (span[0] <= textIndex && textIndex <= span[1]) { return value } } return null } private fun launchBrowser(link: String) { try { val browser = PlatformUI.getWorkbench().browserSupport.externalBrowser browser.openURL(URL(link)) } catch (ex: Exception) { ex.printStackTrace() } } fun showOn(widget: StyledText) { val text = buffer.toString() widget.text = text val ranges = ArrayList() var span: IntArray for (i in headingSpans.indices) { span = headingSpans[i] ranges.add(StyleRange(span[0], span[1] - span[0], headingColor, background, SWT.BOLD)) } for (spn in linksBySpan.keys) { val style = StyleRange(spn[0], spn[1] - spn[0], headingColor, background, SWT.UNDERLINE_LINK) style.underline = true ranges.add(style) } val crStr = Character.toString(CR) var sr: StyleRange for (i in codeSpans.indices) { span = codeSpans[i] sr = StyleRange(codeStyle) sr.start = span[0] sr.length = span[1] - span[0] val colorRanges = codeStyleExtractor.stylesFor(text, sr.start, sr.length, crStr) ranges += colorRanges } // must be in order! val styles = sort(ranges) widget.styleRanges = styles } private fun sort(ranges: List): Array { val styles = ranges.toTypedArray() Arrays.sort(styles) { sr1, sr2 -> sr1.start - sr2.start } return styles } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/FontBuilder.kt ================================================ 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) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StringArranger.kt ================================================ package com.alibaba.smartfox.eclipse.ui.pmd import net.sourceforge.pmd.util.StringUtil import java.util.ArrayList /** * @author Brian Remedios */ class StringArranger(private val indentString: String) { fun withIndent(rawText: String): String { return indentString + rawText } fun format(rawText: String): StringBuilder { val sb = StringBuilder() for (line in trimmedLinesIn(rawText)) { sb.append(indentString) sb.append(line).append(CR) } return sb } fun trimmedLinesIn(text: String): List { val lines = text.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() if (lines.isEmpty()) { return emptyList() } val lineSet = ArrayList(lines.size) var startLine = 0 while (startLine < lines.size && StringUtil.isEmpty(lines[startLine])) { startLine++ } var endLine = lines.size - 1 while (endLine >= 0 && StringUtil.isEmpty(lines[endLine])) { endLine-- } lines.mapTo(lineSet) { it.trim { it <= ' ' } } return lineSet } companion object { private val CR = '\n' } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/StyleExtractor.kt ================================================ package com.alibaba.smartfox.eclipse.ui.pmd import net.sourceforge.pmd.util.StringUtil import org.eclipse.swt.SWT import org.eclipse.swt.custom.StyleRange import org.eclipse.swt.widgets.Display import java.util.ArrayList import java.util.LinkedList /** * @author Brian Remedios */ open class StyleExtractor(private var syntaxData: SyntaxData?) { private val commentOffsets: MutableList init { commentOffsets = LinkedList() } fun syntax(theSyntax: SyntaxData?) { syntaxData = theSyntax } /** * Refreshes the offsets for all multiline comments in the parent * StyledText. The parent StyledText should call this whenever its text is * modified. Note that this code doesn't ignore comment markers inside * strings. * @param text the text from the StyledText */ fun refreshMultilineComments(text: String) { // Clear any stored offsets commentOffsets.clear() if (syntaxData != null) { // Go through all the instances of COMMENT_START var pos = text.indexOf(syntaxData!!.multiLineCommentStart!!) while (pos > -1) { // offsets[0] holds the COMMENT_START offset // and COMMENT_END holds the ending offset val offsets = IntArray(2) offsets[0] = pos // Find the corresponding end comment. pos = text.indexOf(syntaxData!!.multiLineCommentEnd!!, pos) // If no corresponding end comment, use the end of the text offsets[1] = if (pos == -1) text.length - 1 else pos + syntaxData!!.multiLineCommentEnd!!.length - 1 pos = offsets[1] // Add the offsets to the collection commentOffsets.add(offsets) pos = text.indexOf(syntaxData!!.multiLineCommentStart!!, pos) } } } /** * Checks to see if the specified section of text begins inside a multiline * comment. Returns the index of the closing comment, or the end of the line * if the whole line is inside the comment. Returns -1 if the line doesn't * begin inside a comment. * @param start the starting offset of the text * @param length the length of the text * @return int */ private fun getBeginsInsideComment(start: Int, length: Int): Int { // Assume section doesn't being inside a comment var index = -1 // Go through the multiline comment ranges var i = 0 val n = commentOffsets.size while (i < n) { val offsets = commentOffsets[i] // If starting offset is past range, quit if (offsets[0] > start + length) { break } // Check to see if section begins inside a comment if (offsets[0] <= start && offsets[1] >= start) { // It does; determine if the closing comment marker is inside // this section index = if (offsets[1] > start + length) start + length else offsets[1] + syntaxData!!.multiLineCommentEnd!!.length - 1 } i++ } return index } private fun isDefinedVariable(text: String): Boolean { return StringUtil.isNotEmpty(text) } private fun atMultiLineCommentStart(text: String, position: Int): Boolean { return text.indexOf(syntaxData!!.multiLineCommentStart!!, position) == position } private fun atStringStart(text: String, position: Int): Boolean { return text.indexOf(syntaxData!!.stringStart!!, position) == position } private fun atVarnameReference(text: String, position: Int): Boolean { return syntaxData!!.varnameReference != null && text.indexOf(syntaxData!!.varnameReference!!, position) == position } private fun atSingleLineComment(text: String, position: Int): Boolean { return syntaxData!!.comment != null && text.indexOf(syntaxData!!.comment!!, position) == position } private fun getKeywordEnd(lineText: String, start: Int): Int { val length = lineText.length val buf = StringBuilder(length) var i = start // Call any consecutive letters a word while (i < length && Character.isLetter(lineText[i])) { buf.append(lineText[i]) i++ } return if (syntaxData!!.isKeyword(buf.toString())) i else 0 - i } /** * Chop up the text into individual lines starting from offset and then * determine the required styles for each. Ensures the offset is properly * accounted for in each. * @param text * @param offset * @param length * @return */ fun stylesFor(text: String, offset: Int, length: Int, lineSeparator: String): List { if (syntaxData == null) { return emptyList() } val content = text.substring(offset, offset + length) val lines = content.split(lineSeparator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val styles = ArrayList() val separatorLength = lineSeparator.length var currentOffset = offset for (line in lines) { val lineLength = line.length val lineStyles = lineStylesFor(line, 0, lineLength) for (sr in lineStyles) { sr.start += currentOffset } styles.addAll(lineStyles) currentOffset += lineLength + separatorLength } return styles } fun lineStylesFor(lineText: String, lineOffset: Int, length: Int): List { val styles = ArrayList() var start = 0 // Check if line begins inside a multiline comment val mlIndex = getBeginsInsideComment(lineOffset, lineText.length) if (mlIndex > -1) { // Line begins inside multiline comment; create the range styles.add(StyleRange(lineOffset, mlIndex - lineOffset, COMMENT_COLOR, COMMENT_BACKGROUND)) start = mlIndex } // Do punctuation, single-line comments, and keywords while (start < length) { // Check for multiline comments that begin inside this line if (atMultiLineCommentStart(lineText, start)) { // Determine where comment ends var endComment = lineText.indexOf(syntaxData!!.multiLineCommentEnd!!, start) // If comment doesn't end on this line, extend range to end of // line if (endComment == -1) { endComment = length } else { endComment += syntaxData!!.multiLineCommentEnd!!.length } styles.add(StyleRange(lineOffset + start, endComment - start, COMMENT_COLOR, COMMENT_BACKGROUND)) start = endComment } else if (atStringStart(lineText, start)) { // Determine where comment ends var endString = lineText.indexOf(syntaxData!!.stringEnd!!, start + 1) // If string doesn't end on this line, extend range to end of // line if (endString == -1) { endString = length } else { endString += syntaxData!!.stringEnd!!.length } styles.add(StyleRange(lineOffset + start, endString - start, STRING_COLOR, COMMENT_BACKGROUND)) start = endString } else if (atSingleLineComment(lineText, start)) { // line comments styles.add(StyleRange(lineOffset + start, length - start, COMMENT_COLOR, COMMENT_BACKGROUND)) start = length } else if (atVarnameReference(lineText, start)) { // variable // references val buf = StringBuilder() var i = start + syntaxData!!.varnameReference!!.length // Call any consecutive letters a word while (i < length && Character.isLetter(lineText[i])) { buf.append(lineText[i]) i++ } // See if the word is a variable if (isDefinedVariable(buf.toString())) { // It's a keyword; create the StyleRange styles.add(StyleRange(lineOffset + start, i - start, REFERENCED_VAR_COLOR, null, SWT.BOLD)) } // Move the marker to the last char (the one that wasn't a // letter) // so it can be retested in the next iteration through the loop start = i } else if (syntaxData!!.isPunctuation(lineText[start])) { // Add range for punctuation styles.add(StyleRange(lineOffset + start, 1, PUNCTUATION_COLOR, null)) ++start } else if (Character.isLetter(lineText[start])) { val kwEnd = getKeywordEnd(lineText, start) // is a keyword if (kwEnd > start) { styles.add(StyleRange(lineOffset + start, kwEnd - start, KEYWORD_COLOR, null)) } // Move the marker to the last char (the one that wasn't a // letter) // so it can be retested in the next iteration through the loop start = Math.abs(kwEnd) } else { ++start // It's nothing we're interested in; advance the marker }// Check for punctuation } return styles } companion object { private val COMMENT_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN) private val REFERENCED_VAR_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN) private val UNREFERENCED_VAR_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW) private val COMMENT_BACKGROUND = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE) private val PUNCTUATION_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_BLACK) private val KEYWORD_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_MAGENTA) private val STRING_COLOR = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxData.kt ================================================ package com.alibaba.smartfox.eclipse.ui.pmd /** * This class contains information for syntax coloring and styling for an * extension */ class SyntaxData(val extension: String) { var varnameReference: String? = null var stringStart: String? = null var stringEnd: String? = null private var keywords: Collection? = null private var punctuation: String? = null var comment: String? = null var multiLineCommentStart: String? = null var multiLineCommentEnd: String? = null fun matches(otherExtension: String): Boolean { return extension == otherExtension } fun isKeyword(word: String): Boolean { return keywords != null && keywords!!.contains(word) } fun isPunctuation(ch: Char): Boolean { return punctuation != null && punctuation!!.indexOf(ch) >= 0 } fun setKeywords(keywords: Collection) { this.keywords = keywords } fun setPunctuation(thePunctuationChars: String) { punctuation = thePunctuationChars } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/ui/pmd/SyntaxManager.kt ================================================ package com.alibaba.smartfox.eclipse.ui.pmd import org.eclipse.swt.custom.StyledText import org.eclipse.swt.events.ModifyListener import java.util.HashSet import java.util.Hashtable import java.util.MissingResourceException import java.util.ResourceBundle import java.util.StringTokenizer /** * This class manages the syntax coloring and styling data */ object SyntaxManager { private val syntaxByExtension = Hashtable() fun adapt(codeField: StyledText, languageCode: String, oldListener: ModifyListener?): ModifyListener? { if (oldListener != null) { codeField.removeModifyListener(oldListener) } val sd = SyntaxManager.getSyntaxData(languageCode) ?: return null val blsl = BasicLineStyleListener(sd) codeField.addLineStyleListener(blsl) val ml = ModifyListener { blsl.refreshMultilineComments(codeField.text) codeField.redraw() } codeField.addModifyListener(ml) return ml } /** * Gets the syntax data for an extension */ @Synchronized fun getSyntaxData(extension: String): SyntaxData? { // Check in cache var sd: SyntaxData? = syntaxByExtension[extension] if (sd == null) { // Not in cache; load it and put in cache sd = loadSyntaxData(extension) if (sd != null) { syntaxByExtension.put(sd.extension, sd) } } return sd } /** * Loads the syntax data for an extension * @return SyntaxData */ private fun loadSyntaxData(filename: String): SyntaxData? { var sd: SyntaxData? = null try { val rb = ResourceBundle.getBundle("/syntax/$filename") sd = SyntaxData(filename) sd.stringStart = rb.getString("stringstart") sd.stringEnd = rb.getString("stringend") sd.multiLineCommentStart = rb.getString("multilinecommentstart") sd.multiLineCommentEnd = rb.getString("multilinecommentend") // Load the keywords val keywords = HashSet() val st = StringTokenizer(rb.getString("keywords"), " ") while (st.hasMoreTokens()) { keywords.add(st.nextToken()) } sd.setKeywords(keywords) // Load the punctuation sd.setPunctuation(rb.getString("punctuation")) if (rb.containsKey("comment")) { sd.comment = rb.getString("comment") } if (rb.containsKey("varnamedelimiter")) { sd.varnameReference = rb.getString("varnamedelimiter") } } catch (e: MissingResourceException) { // Ignore } return sd } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/CleanUps.kt ================================================ // ===================================================================== // // Copyright (C) 2012 - 2016, Philip Graf // // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // ===================================================================== package com.alibaba.smartfox.eclipse.util import com.alibaba.smartfox.eclipse.SmartfoxActivator import org.eclipse.core.filebuffers.FileBuffers import org.eclipse.core.filebuffers.ITextFileBuffer import org.eclipse.core.filebuffers.LocationKind import org.eclipse.core.resources.IFile import org.eclipse.core.runtime.Assert import org.eclipse.core.runtime.CoreException import org.eclipse.core.runtime.IProgressMonitor import org.eclipse.core.runtime.IStatus import org.eclipse.core.runtime.NullProgressMonitor import org.eclipse.core.runtime.Status import org.eclipse.jdt.core.ICompilationUnit import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.JavaCore import org.eclipse.jdt.core.dom.CompilationUnit import org.eclipse.jdt.internal.corext.fix.CleanUpConstants import org.eclipse.jdt.internal.corext.fix.CleanUpRefactoring import org.eclipse.jdt.internal.corext.fix.FixMessages import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser import org.eclipse.jdt.internal.ui.JavaPlugin import org.eclipse.jdt.internal.ui.actions.ActionUtil import org.eclipse.jdt.internal.ui.fix.MapCleanUpOptions import org.eclipse.jdt.ui.SharedASTProvider import org.eclipse.jdt.ui.cleanup.CleanUpContext import org.eclipse.jdt.ui.cleanup.CleanUpOptions import org.eclipse.jdt.ui.cleanup.ICleanUp import org.eclipse.jface.text.BadLocationException import org.eclipse.jface.text.IDocument import org.eclipse.jface.text.IDocumentExtension4 import org.eclipse.jface.window.Window import org.eclipse.ltk.core.refactoring.Change import org.eclipse.ltk.core.refactoring.CompositeChange import org.eclipse.ltk.core.refactoring.IRefactoringCoreStatusCodes import org.eclipse.ltk.core.refactoring.NullChange import org.eclipse.ltk.core.refactoring.PerformChangeOperation import org.eclipse.ltk.core.refactoring.RefactoringCore import org.eclipse.ltk.core.refactoring.RefactoringStatus import org.eclipse.ltk.core.refactoring.TextFileChange import org.eclipse.ltk.ui.refactoring.RefactoringUI import org.eclipse.text.edits.MalformedTreeException import org.eclipse.text.edits.TextEdit import org.eclipse.text.edits.UndoEdit import org.eclipse.ui.PlatformUI import java.util.ArrayList import java.util.HashMap import java.util.LinkedList /** * * * @author caikang * @date 2017/06/15 */ object CleanUps { private val WARNING_VALUE = "warning" private val ERROR_VALUE = "error" val cleanUpSettings = mapOf(CleanUpConstants.ADD_MISSING_ANNOTATIONS to CleanUpOptions.TRUE, CleanUpConstants.CONTROL_STATMENTS_USE_BLOCKS_ALWAYS to CleanUpOptions.TRUE, CleanUpConstants.ADD_MISSING_ANNOTATIONS_OVERRIDE to CleanUpOptions.TRUE, CleanUpConstants.CONTROL_STATEMENTS_USE_BLOCKS to CleanUpOptions.TRUE, CleanUpConstants.ADD_MISSING_ANNOTATIONS_OVERRIDE_FOR_INTERFACE_METHOD_IMPLEMENTATION to CleanUpOptions.TRUE) fun fix(file: IFile, monitor: IProgressMonitor) { val compilationUnit = JavaCore.createCompilationUnitFrom(file) ?: return doCleanUp(compilationUnit, monitor) } @Throws(CoreException::class) fun doCleanUp(unit: ICompilationUnit, monitor: IProgressMonitor) { monitor.beginTask("Fix", IProgressMonitor.UNKNOWN) if (!ActionUtil.isOnBuildPath(unit)) return val result = CompositeChange(FixMessages.CleanUpPostSaveListener_SaveAction_ChangeName) val undoEdits = LinkedList() val oldFileValue = unit.resource.modificationStamp val oldDocValue = getDocumentStamp(unit.resource as IFile, monitor) val manager = RefactoringCore.getUndoManager() var success = false try { manager.aboutToPerformChange(result) success = doCleanUp(unit, monitor, result, undoEdits) } finally { manager.changePerformed(result, success) } if (undoEdits.size > 0) { val undoEditArray = undoEdits.toTypedArray() val undo = CleanUpSaveUndo(result.name, unit.resource as IFile, undoEditArray, oldDocValue, oldFileValue) undo.initializeValidationData(NullProgressMonitor()) manager.addUndo(result.name, undo) } } @Throws(CoreException::class) private fun getDocumentStamp(file: IFile, monitor: IProgressMonitor): Long { val manager = FileBuffers.getTextFileBufferManager() val path = file.fullPath monitor.beginTask("", 2) var buffer: ITextFileBuffer? = null try { manager.connect(path, LocationKind.IFILE, monitor) buffer = manager.getTextFileBuffer(path, LocationKind.IFILE) val document = buffer!!.document if (document is IDocumentExtension4) { return document.modificationStamp } else { return file.modificationStamp } } finally { if (buffer != null) manager.disconnect(path, LocationKind.IFILE, monitor) monitor.done() } } private fun doCleanUp(unit: ICompilationUnit, monitor: IProgressMonitor, result: CompositeChange, undoEdits: LinkedList): Boolean { val cleanUps = JavaPlugin.getDefault().cleanUpRegistry.createCleanUps( setOf("org.eclipse.jdt.ui.cleanup.java50", "org.eclipse.jdt.ui.cleanup.control_statements")) val preCondition = RefactoringStatus() val postCondition = RefactoringStatus() cleanUps.forEach { cleanUp -> cleanUp.setOptions(MapCleanUpOptions(cleanUpSettings)) preCondition.merge(cleanUp.checkPreConditions(unit.javaProject, arrayOf(unit), monitor)) val options = HashMap(cleanUp.requirements.compilerOptions ?: emptyMap()) var ast = CleanUps.createAst(unit, options, monitor) if (cleanUp.requirements.requiresAST()) { ast = createAst(unit, options, monitor) } val context = CleanUpContext(unit, ast) val undoneCleanUps = ArrayList() val change = CleanUpRefactoring.calculateChange(context, arrayOf(cleanUp), undoneCleanUps, null) postCondition.merge(cleanUp.checkPostConditions(monitor)) if (showStatus(postCondition) != Window.OK) { return@doCleanUp false } if (change == null) { return@forEach } result.add(change) change.initializeValidationData(NullProgressMonitor()) val performChangeOperation = PerformChangeOperation(change) performChangeOperation.setSchedulingRule(unit.schedulingRule) performChangeOperation.run(monitor) performChangeOperation.undoChange undoEdits.addFirst(change.undoEdit) } return true } private fun showStatus(status: RefactoringStatus): Int { if (!status.hasError()) return Window.OK val shell = PlatformUI.getWorkbench().activeWorkbenchWindow.shell val dialog = RefactoringUI.createRefactoringStatusDialog(status, shell, "", false) return dialog.open() } private fun createAst(unit: ICompilationUnit, cleanUpOptions: Map, monitor: IProgressMonitor): CompilationUnit { val project = unit.javaProject if (compatibleOptions(project, cleanUpOptions)) { val ast = SharedASTProvider.getAST(unit, SharedASTProvider.WAIT_NO, monitor) if (ast != null) return ast } val parser = CleanUpRefactoring.createCleanUpASTParser() parser.setSource(unit) val compilerOptions = RefactoringASTParser.getCompilerOptions(unit.javaProject) compilerOptions.putAll(cleanUpOptions) parser.setCompilerOptions(compilerOptions) return parser.createAST(monitor) as CompilationUnit } private fun compatibleOptions(project: IJavaProject, cleanUpOptions: Map): Boolean { if (cleanUpOptions.isEmpty()) { return true } val projectOptions = project.getOptions(true) return !cleanUpOptions.keys.any { val projectOption = projectOptions[it]?.toString() val cleanUpOption = cleanUpOptions[it]?.toString() !strongerEquals(projectOption, cleanUpOption) } } private fun strongerEquals(projectOption: String?, cleanUpOption: String?): Boolean { if (projectOption == null) return false if (ERROR_VALUE == cleanUpOption) { return ERROR_VALUE == projectOption } else if (WARNING_VALUE == cleanUpOption) { return ERROR_VALUE == projectOption || WARNING_VALUE == projectOption } return false } private class CleanUpSaveUndo(name: String, private val fFile: IFile, private val fUndos: Array, private val fDocumentStamp: Long, private val fFileStamp: Long) : TextFileChange(name, fFile) { init { Assert.isNotNull(fUndos) } public override fun needsSaving(): Boolean { return true } @Throws(CoreException::class) override fun perform(monitor: IProgressMonitor?): Change { val pm = monitor ?: NullProgressMonitor() if (isValid(pm).hasFatalError()) return NullChange() val manager = FileBuffers.getTextFileBufferManager() pm.beginTask("", 2) var buffer: ITextFileBuffer? = null try { manager.connect(fFile.fullPath, LocationKind.IFILE, pm) buffer = manager.getTextFileBuffer(fFile.fullPath, LocationKind.IFILE) val document = buffer!!.document val oldFileValue = fFile.modificationStamp val undoEditCollector = LinkedList() val oldDocValue = LongArray(1) val setContentStampSuccess = booleanArrayOf(false) if (!buffer.isSynchronizationContextRequested) { performEdit(document, oldFileValue, undoEditCollector, oldDocValue, setContentStampSuccess) } else { val fileBufferManager = FileBuffers.getTextFileBufferManager() class UIRunnable : Runnable { var fDone: Boolean = false var fException: Exception? = null override fun run() { synchronized(this) { try { performEdit(document, oldFileValue, undoEditCollector, oldDocValue, setContentStampSuccess) } catch (e: BadLocationException) { fException = e } catch (e: MalformedTreeException) { fException = e } catch (e: CoreException) { fException = e } finally { fDone = true (this as Object).notifyAll() } } } } val runnable = UIRunnable() synchronized(runnable) { fileBufferManager.execute(runnable) while (!runnable.fDone) { try { (runnable as Object).wait(500) } catch (x: InterruptedException) { } } } if (runnable.fException != null) { if (runnable.fException is BadLocationException) { throw runnable.fException as BadLocationException } else if (runnable.fException is MalformedTreeException) { throw runnable.fException as MalformedTreeException } else if (runnable.fException is CoreException) { throw runnable.fException as CoreException } } } buffer.commit(pm, false) if (!setContentStampSuccess[0]) { fFile.revertModificationStamp(fFileStamp) } return CleanUpSaveUndo(name, fFile, undoEditCollector.toTypedArray(), oldDocValue[0], oldFileValue) } catch (e: BadLocationException) { throw wrapBadLocationException(e) } finally { if (buffer != null) manager.disconnect(fFile.fullPath, LocationKind.IFILE, pm) } } @Throws(MalformedTreeException::class, BadLocationException::class, CoreException::class) private fun performEdit(document: IDocument, oldFileValue: Long, editCollector: LinkedList, oldDocValue: LongArray, setContentStampSuccess: BooleanArray) { if (document is IDocumentExtension4) { oldDocValue[0] = document.modificationStamp } else { oldDocValue[0] = oldFileValue } // perform the changes fUndos.map { it.apply(document, TextEdit.CREATE_UNDO) }.forEach { editCollector.addFirst(it) } if (document is IDocumentExtension4 && fDocumentStamp != IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { try { document.replace(0, 0, "", fDocumentStamp) setContentStampSuccess[0] = true } catch (e: BadLocationException) { throw wrapBadLocationException(e) } } } } private fun wrapBadLocationException(e: BadLocationException): CoreException { var message: String? = e.message if (message == null) message = "BadLocationException" return CoreException( Status(IStatus.ERROR, SmartfoxActivator.PLUGIN_ID, IRefactoringCoreStatusCodes.BAD_LOCATION, message, e)) } } ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/kotlin/com/alibaba/smartfox/eclipse/util/MarkerUtil.kt ================================================ // ===================================================================== // // 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) ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle.xml ================================================ 阿里编码规约扫描 切换语言至英文(English) 切换语言至中文 切换到English成功,是否重启 切换到中文成功,是否重启 ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/messages/P3cBundle_en.xml ================================================ Alibaba Coding Guidelines Analyze Switch language to English Switch language to Chinese Switch language to English success,restart? Switch language to Chinese success,restart? ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-pmd.xml ================================================ ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/rulesets/java/ali-ruleOnEclipse.xml ================================================ rule.standalone.MissingOverrideAnnotationRule.desc 1 1 rule.standalone.AvoidUseDeprecationRule.desc 2 rule.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsRule.desc 2 ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.plugin/src/main/resources/syntax/java.properties ================================================ # 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 ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/category.xml ================================================ Alibaba Java Coding Guidelines ================================================ FILE: eclipse-plugin/com.alibaba.smartfox.eclipse.updatesite/pom.xml ================================================ 4.0.0 com.alibaba.smartfox.eclipse smartfox-eclipse 2.0.1-SNAPSHOT com.alibaba.smartfox.eclipse.updatesite eclipse-repository 2017 ================================================ FILE: eclipse-plugin/pom.xml ================================================ 4.0.0 com.alibaba.smartfox.eclipse smartfox-eclipse 2.0.1-SNAPSHOT pom 2017 1.0.0 ${tycho.version} http://download.eclipse.org/releases/neon UTF-8 http://download.eclipse.org/tools/ajdt/46/dev/update 1.3.30 juno com.alibaba.smartfox.eclipse.plugin com.alibaba.smartfox.eclipse.feature com.alibaba.smartfox.eclipse.updatesite junit junit 4.11 test org.jetbrains.kotlin kotlin-stdlib-jdk8 ${kotlin.version} compile juno p2 http://mirrors.ustc.edu.cn/eclipse/releases/juno/ sonatype-nexus-snapshots Sonatype Nexus Snapshots https://oss.sonatype.org/content/repositories/snapshots false true org.eclipse.tycho tycho-maven-plugin ${tycho.version} true org.eclipse.tycho target-platform-configuration linux gtk x86 linux gtk x86_64 win32 win32 x86 win32 win32 x86_64 macosx cocoa x86_64 org.eclipse.tycho target-platform-configuration ${tycho.version} org.eclipse.tycho tycho-compiler-plugin ${tycho.version} 1.8 UTF-8 -err:-forbidden org.codehaus.mojo aspectj-maven-plugin ${aspectj.plugin.version} org.aspectj aspectjtools ${aspectj.version} org.eclipse.tycho tycho-packaging-plugin ${tycho.version} org.eclipse.tycho tycho-surefire-plugin ${tycho.version} true org.eclipse.tycho tycho-source-plugin ${tycho.version} false org.eclipse.tycho tycho-p2-director-plugin ${tycho.version} org.eclipse.tycho tycho-p2-repository-plugin ${tycho.version} smartfox-eclipse-plugin org.eclipse.tycho.extras tycho-source-feature-plugin ${tycho-extras.version} org.eclipse.tycho.extras tycho-custom-bundle-plugin ${tycho-extras.version} org.eclipse.tycho tycho-p2-plugin ${tycho.version} warn none http://download.eclipse.org/eclipse/updates/4.4 ================================================ FILE: idea-plugin/.gitignore ================================================ # 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 ================================================ FILE: idea-plugin/README.md ================================================ # Idea Plugin --- ## Prepare - Project JDK: 1.7+ - Gradle: 3.0+(Require JDK1.8+ for gradle) ## Build ``` cd p3c-idea ../gradlew clean buildPlugin ``` ## Run plugin ``` cd p3c-idea ../gradlew runIde # run specific IDEA ../gradlew runIde -Pidea_version=2018.3 ``` ## Use p3c-common as your plugin dependency ```groovy compile 'com.alibaba.p3c.idea:p3c-common:1.0.0' ``` ## [中文使用手册](README_cn.md) ## Install ### Install from repositories 1. Settings >> Plugins >> Browse repositories... ![Switch language](doc/images/install_1.png) 2. Search plugin by keyword 'alibaba' then install 'Alibaba Java Coding Guidelines' plugin ![Switch language](doc/images/install_2.png) 3. Restart to take effect. ### Install from local zip file. 1. Open https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines and download the latest version zip file. ![download](https://gw.alicdn.com/tfscom/TB1WcF3hzlxYKJjSZFuXXaYlVXa.png) 2. Settings >> Plugins >> Install plugin from disk...,select the downloaded zip file in previous step then restart your idea ![](https://gw.alicdn.com/tfscom/TB1WFsKiqigSKJjSsppXXabnpXa.png) ## Use 1. Switch language ![Switch language](doc/images/switch_language.png) 2. Inspections ![Real time](doc/images/inspection.png) ![Settings](doc/images/inspection_setting.png) 3. Code Analyze ![Settings](doc/images/analyze.png) We use the idea standard Inspection Results to show our violations. ![Result](doc/images/inspection_result.png) We can also analyze file which is modified before vcs checkin. ![Before Checkin](doc/images/analyze_before_checkin.png) ## Other 1. [中文乱码解决方法](https://github.com/alibaba/p3c/issues/32#issuecomment-336762512) * Appearance&Behavior -> Appearance -> UI Options -> Name 里面设置成微软雅黑(microsoft yahei light)   ![Font](doc/images/change_name.png) * Switch Language to English and restart. ![Switch language](doc/images/normal_view.png) ================================================ FILE: idea-plugin/README_cn.md ================================================ > 首先非常感谢大家对插件的支持与意见,英文版的文档还是略为简单,这里详细介绍一下插件的安装使用。 ## 插件安装 ### 通过Jetbrains官方仓库安装 1. 打开 Settings >> Plugins >> Browse repositories... ![](https://gw.alicdn.com/tfscom/TB1Qn83ifBNTKJjy1zdXXaScpXa.png) 2. 在搜索框输入alibaba即可看到Alibaba Java Code Guidelines插件,点击Install进行安装,然后重启IDE生效 `注意:因为插件zip包托管在Jetbrains官方CDN上,所以是从国外的服务器进行下载,可能会出现超时的情况` ![](https://gw.alicdn.com/tfscom/TB1vcGbmYsTMeJjy1zcXXXAgXXa.png) ### 通过下载安装包进行安装 1. 打开[插件](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines)页面 ![download](https://gw.alicdn.com/tfscom/TB1WcF3hzlxYKJjSZFuXXaYlVXa.png) 2. Settings >> Plugins >> Install plugin from disk...,选择刚刚下载的zip包安装,然后重启IDE ![](https://gw.alicdn.com/tfscom/TB1WFsKiqigSKJjSsppXXabnpXa.png) ### 注意 最低支持IDEA版本为14.1(buildNumber 141.0,可以在About Intellij IDEA中查看版本信息),使用IDEA14的同学最好升级到14.1.7(历史版本传送门) 插件基于JDK1.7打包,所以IDEA启动时使用的JDK版本如果是1.6的话就会报Unsupported major.minor version 51.0异常,建议大家都升级一下。 ### [中文乱码解决方法](https://github.com/alibaba/p3c/issues/32#issuecomment-336762512) 1. 修改字体——Appearance&Behavior -> Appearance -> UI Options -> Name 里面设置成中文字体——如微软雅黑(microsoft yahei light)、文泉驿(linux) ![](https://gw.alicdn.com/tfscom/TB14wTmm3oQMeJjy0FoXXcShVXa.png) 2. Switch Language to English and restart. ![](https://gw.alicdn.com/tfscom/TB1Z6u1mYsTMeJjSszhXXcGCFXa.png) ## 插件使用 目前插件实现了开发手册中的的53条规则,大部分基于PMD实现,其中有4条规则基于IDEA实现,并且基于IDEA Inspection实现了实时检测功能。部分规则实现了Quick Fix功能,对于可以提供Quick Fix但没有提供的,我们会尽快实现,也欢迎有兴趣的同学加入进来一起努力。 目前插件检测有两种模式:实时检测、手动触发。 ### 实时检测 实时检测功能会在开发过程中对当前文件进行检测,并以高亮的形式提示出来,同时也可以支持Quick Fix,该功能默认开启,可以通过配置关闭。 #### 结果高亮提示

检测结果高亮提示,并且鼠标放上去会弹出提示信息。

![](https://gw.alicdn.com/tfscom/TB17wt3mYsTMeJjSszdXXcEupXa.png) ![](https://gw.alicdn.com/tfscom/TB1Rq85ifNNTKJjSspkXXaeWFXa.png) #### Intention QuickFix功能 Alt+Enter键可呼出Intention菜单,不同的规则会提示不同信息的Quick Fix按钮 ![](https://gw.alicdn.com/tfscom/TB1twLMsOAKL1JjSZFoXXagCFXa.png) #### 关闭实时检测 在某些情况下,我们不希望对代码提示违规信息,比如我们在阅读Github开源项目代码的时候,如果界面出现一堆红色、黄色的提示,此时心里肯定是飘过一万只草泥马。这个时候我们可以通过Inspection的设置关闭实时检测功能。 1. 通过右键快速关闭(打开)所有规则的实时检测功能 ![](https://gw.alicdn.com/tfscom/TB1dBbDe_1z01JjSZFCXXXY.XXa.png) 2. 通过Settings >> Editor >> Inspections 进行手动设置 ![](https://gw.alicdn.com/tfscom/TB1zhCBsiFTMKJjSZFAXXckJpXa.png) 也可以关闭某条规则的实时检测功能或者修改提示级别。 ### 代码扫描 可以通过右键菜单、Toolbar按钮、快捷键三种方式手动触发代码检测。同时结果面板中可以对部分实现了QuickFix功能的规则进行快速修复。 #### 触发扫描 在当前编辑的文件中点击右键,可以在弹出的菜单中触发对该文件的检测。 ![](https://gw.alicdn.com/tfscom/TB1Wj49mYsTMeJjSszdXXcEupXa.png) 在左侧的Project目录树种点击右键,可以触发对整个工程或者选择的某个目录、文件进行检测。 ![](https://gw.alicdn.com/tfscom/TB1h_XciWmgSKJjSspiXXXyJFXa.png) 如果您打开了IDE的Toolbar,也可以通过Toolbar中的按钮来触发检测,目前Toolbar的按钮触发的检测范围与您IDE当时的焦点有关,如当前编辑的文件或者是Project目录树选中的项,是不是感觉与右键菜单的检测范围类似呢。 ![](https://gw.alicdn.com/tfscom/TB1q3Nfi6uhSKJjSspmXXcQDpXa.png) 使用快捷键(Ctrl+Shift+Alt+J)触发弹出窗口,选择检测范围;您也可自定义快捷键。 ![](https://gw.alicdn.com/tfscom/TB1k4uXmYwTMeJjSszfXXXbtFXa.png) ![](https://gw.alicdn.com/tfscom/TB1ObqXifxNTKJjy0FjXXX6yVXa.png) #### 扫描结果 检测结果直接使用IDEA Run Inspection By Name功能的结果界面,插件的检测结果分级为Blocker、Critical、Major。默认按等级分组,方便统计每个级别错误的数量。 ![](https://gw.alicdn.com/tfscom/TB1aC1yifJNTKJjSspoXXc6mpXa.png) 默认情况我们在结果面板需要双击具体违规项才能打开对应的源文件,开启Autoscroll To Source选项,单击面板中的文件名、或者是具体的违规项的时候IDEA会自动打开对应的源文件。 ![](https://gw.alicdn.com/tfscom/TB1aIixmYsTMeJjy1zcXXXAgXXa.png) #### QuickFix 对于实现Quick Fix的规则,在结果面板中可以直接一键修复 `注意:IDEA14、15可以通过左下角的灯泡进行一键修复操作。` ![](https://gw.alicdn.com/tfscom/TB1Kw5Vm3oQMeJjy0FpXXcTxpXa.png) ![](https://gw.alicdn.com/tfscom/TB1lHZZiGagSKJjy0FbXXa.mVXa.png) #### 其他 面板中其他按钮的功能大家自行探索吧,就不一一赘述了 ### 代码提交时检测 1. 在提交代码框勾选Alibaba Code Guidelines选项 ![](https://gw.alicdn.com/tfscom/TB1u_ZZjamgSKJjSspiXXXyJFXa.png) 2. 如果有违反手册的地方会提示是否继续提交,选择取消后会自动对修改的代码进行扫描 ![](https://gw.alicdn.com/tfscom/TB1r5PUXbb85uJjSZFmXXcgsFXa.png) ================================================ FILE: idea-plugin/build.gradle ================================================ 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.8 compileJava.options.encoding = 'UTF-8' configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } repositories { jcenter() mavenCentral() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testCompile group: 'junit', name: 'junit', version: '4.11' } } ================================================ FILE: idea-plugin/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: idea-plugin/gradle.properties ================================================ kotlin_version=1.3.72 idea_version=2018.3 plugin_name=Alibaba Java Coding Guidelines gradle_jetbrains_version=0.4.5 systemProp.file.encoding=UTF-8 plugin_version=2.1.0 ================================================ FILE: idea-plugin/gradlew ================================================ #!/usr/bin/env sh # # Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## ## ## 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='"-Xmx64m" "-Xms64m"' # 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 or MSYS, switch paths to Windows format before running java if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" exec "$JAVACMD" "$@" ================================================ FILE: idea-plugin/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @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 Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @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="-Xmx64m" "-Xms64m" @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 ================================================ FILE: idea-plugin/p3c-common/build.gradle ================================================ plugins { id "org.jetbrains.intellij" version '0.4.5' } 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 } def myPlugins = [] def versionDotIndex = idea_version.indexOf('.') def intVersion = versionDotIndex == -1 ? Integer.parseInt(idea_version) : Integer.parseInt(idea_version.substring(0, versionDotIndex)) if (intVersion >= 2019 || (intVersion < 1000 && intVersion >= 193)) { myPlugins = ['java'] } intellij { version idea_version plugins = myPlugins pluginName plugin_name updateSinceUntilBuild false sandboxDirectory "$project.buildDir/idea-sandbox/$idea_version" } version '2.0.1' ext.isReleaseVersion = !version.endsWith("SNAPSHOT") dependencies { compile group: 'org.freemarker', name: 'freemarker', version: '2.3.25-incubating' compile 'com.alibaba.p3c:p3c-pmd:2.1.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: findProperty('ossrhUsername'), password: findProperty('ossrhPassword')) } snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { authentication(userName: findProperty('ossrhUsername'), password: findProperty('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 { required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } sign configurations.archives } ================================================ FILE: idea-plugin/p3c-common/src/main/java/icons/P3cIcons.java ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES 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 final class P3cIcons { private P3cIcons() { throw new AssertionError("icons.P3cIcons" + " instances for you!"); } public static final Icon ANALYSIS_ACTION = IconLoader.getIcon("/icons/ali-ide-run.png"); public static final Icon PROJECT_INSPECTION_ON = IconLoader.getIcon("/icons/qiyong.png"); public static final Icon PROJECT_INSPECTION_OFF = IconLoader.getIcon("/icons/tingyong.png"); public static final Icon LANGUAGE = IconLoader.getIcon("/icons/language.png"); public static final Icon ALIBABA = IconLoader.getIcon("/icons/alibaba.png"); } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/AliInspectionAction.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.p3c.idea.util.NumberConstants import com.google.common.collect.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.InspectionsBundle import com.intellij.codeInspection.ex.GlobalInspectionContextImpl import com.intellij.codeInspection.ex.InspectionManagerEx import com.intellij.codeInspection.ex.InspectionToolWrapper import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys 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.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.wm.ToolWindowId import com.intellij.openapi.wm.ToolWindowManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiFileSystemItem 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(CommonDataKeys.PSI_ELEMENT) val psiFile = e.getData(CommonDataKeys.PSI_FILE) val virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE) val virtualFiles = e.getData>(CommonDataKeys.VIRTUAL_FILE_ARRAY) var analysisScope: AnalysisScope? = null var projectDir = false if (psiFile != null) { analysisScope = AnalysisScope(psiFile) projectDir = isBaseDir(psiFile.virtualFile, project) } else if (virtualFiles != null && virtualFiles.size > NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { analysisScope = AnalysisScope(project, Lists.newArrayList(*virtualFiles)) projectDir = virtualFiles.any { isBaseDir(it, project) } } else { if (virtualFile != null && virtualFile.isDirectory) { val psiDirectory = PsiManager.getInstance(project).findDirectory(virtualFile) if (psiDirectory != null) { analysisScope = AnalysisScope(psiDirectory) projectDir = isBaseDir(virtualFile, project) } } if (analysisScope == null && virtualFile != null) { analysisScope = AnalysisScope(project, listOf(virtualFile)) projectDir = isBaseDir(virtualFile, project) } if (analysisScope == null) { projectDir = true analysisScope = AnalysisScope(project) } } if (e.inputEvent is KeyEvent) { inspectForKeyEvent(project, managerEx, toolWrappers, psiElement, psiFile, virtualFile, analysisScope) return } val element = psiFile ?: psiElement analysisScope.isIncludeTestSource = false analysisScope.setSearchInLibraries(true) createContext( toolWrappers, managerEx, element, projectDir, analysisScope ).doInspections(analysisScope) } private fun isBaseDir(file: VirtualFile, project: Project): Boolean { if (file.canonicalPath == null || project.basePath == null) { return false } return project.basePath == file.canonicalPath } private fun inspectForKeyEvent( project: Project, managerEx: InspectionManagerEx, toolWrappers: List>, psiElement: PsiElement?, psiFile: PsiFile?, virtualFile: VirtualFile?, analysisScope: AnalysisScope ) { var module: Module? = null if (virtualFile != null && project.baseDir != virtualFile) { module = ModuleUtilCore.findModuleForFile(virtualFile, project) } val uiOptions = AnalysisUIOptions.getInstance(project) uiOptions.ANALYZE_TEST_SOURCES = false val dialog = BaseAnalysisActionDialog( "Select Analyze Scope", "Analyze Scope", project, analysisScope, module?.name, 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, scope ).doInspections(scope) } override fun update(e: AnActionEvent) { e.presentation.text = P3cBundle.getMessage("com.alibaba.p3c.idea.action.AliInspectionAction.text") } companion object { private val logger = Logger.getInstance(AliInspectionAction::class.java) private fun getTitle(element: PsiElement?, isProjectScopeSelected: Boolean): String? { if (element == null) { return null } if (isProjectScopeSelected) { return "Project" } if (element is PsiFileSystemItem) { return VfsUtilCore.getRelativePath(element.virtualFile, element.project.baseDir) } return null } fun createContext( toolWrapperList: List>, managerEx: InspectionManagerEx, psiElement: PsiElement?, projectScopeSelected: Boolean, scope: AnalysisScope ): GlobalInspectionContextImpl { // remove last same scope content val project = managerEx.project val title = getTitle(psiElement, projectScopeSelected) val model = InspectionProfileService.createSimpleProfile(toolWrapperList, managerEx, psiElement) title?.let { model.name = it } val inspectionContext = createNewGlobalContext( managerEx, projectScopeSelected ) InspectionProfileService.setExternalProfile(model, inspectionContext) val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.INSPECTION) if (toolWindow != null) { val contentManager = toolWindow.contentManager val contentTitle = title?.let { InspectionsBundle.message("inspection.results.for.profile.toolwindow.title", it, scope.shortenName) } val content = contentManager.contents.firstOrNull { contentTitle != null && (it.tabName == contentTitle || it.tabName.endsWith(contentTitle)) } content?.let { contentManager.removeContent(content, true) } } return inspectionContext } private fun createNewGlobalContext( managerEx: InspectionManagerEx, projectScopeSelected: Boolean ): GlobalInspectionContextImpl { return PmdGlobalInspectionContextImpl(managerEx.project, managerEx.contentManager, projectScopeSelected) } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/PmdGlobalInspectionContextImpl.kt ================================================ package com.alibaba.p3c.idea.action import com.alibaba.p3c.idea.component.AliProjectComponent import com.alibaba.p3c.idea.ep.InspectionActionExtensionPoint import com.alibaba.p3c.idea.inspection.AliLocalInspectionToolProvider import com.alibaba.p3c.idea.inspection.PmdRuleInspectionIdentify import com.alibaba.p3c.idea.pmd.AliPmdProcessor import com.intellij.analysis.AnalysisScope import com.intellij.codeInsight.daemon.ProblemHighlightFilter import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator import com.intellij.codeInspection.ex.GlobalInspectionContextImpl import com.intellij.codeInspection.ui.InspectionResultsView import com.intellij.concurrency.JobLauncher import com.intellij.concurrency.JobLauncherImpl import com.intellij.concurrency.SensitiveProgressWrapper import com.intellij.diagnostic.ThreadDumper import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ReadAction import com.intellij.openapi.application.ex.ApplicationManagerEx import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicatorProvider import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task.Backgroundable import com.intellij.openapi.progress.impl.CoreProgressManager import com.intellij.openapi.progress.util.ProgressIndicatorUtils import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.project.displayUrlRelativeToProject import com.intellij.openapi.project.isProjectOrWorkspaceFile import com.intellij.openapi.roots.FileIndex import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.EmptyRunnable import com.intellij.openapi.util.NotNullLazyValue import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiBinaryFile import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import com.intellij.psi.SingleRootFileViewProvider import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.SearchScope import com.intellij.psi.util.PsiUtilCore import com.intellij.ui.content.ContentManager import com.intellij.util.ExceptionUtil import com.intellij.util.IncorrectOperationException import com.intellij.util.Processor import com.intellij.util.ReflectionUtil import com.intellij.util.containers.ContainerUtil import gnu.trove.THashSet import net.sourceforge.pmd.RuleViolation import java.util.Queue import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.BlockingQueue import java.util.concurrent.Future import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit.SECONDS /** * @date 2020/06/19 * @author caikang */ class PmdGlobalInspectionContextImpl( project: Project, contentManager: NotNullLazyValue, private val projectScopeSelected: Boolean ) : GlobalInspectionContextImpl(project, contentManager) { private val logger = Logger.getInstance(PmdGlobalInspectionContextImpl::class.java) override fun runTools(scope: AnalysisScope, runGlobalToolsOnly: Boolean, isOfflineInspections: Boolean) { val usedTools = usedTools val hasPmdTools = usedTools.any { it.tool.tool is PmdRuleInspectionIdentify } if (hasPmdTools) { val progressIndicator = ProgressIndicatorProvider.getGlobalProgressIndicator() ?: throw IncorrectOperationException("Must be run under progress") pmdNodeWarmUp(scope, progressIndicator, isOfflineInspections) } super.runTools(scope, runGlobalToolsOnly, isOfflineInspections) if (myProgressIndicator.isCanceled) { return } InspectionActionExtensionPoint.extension.extensions.forEach { try { it.doOnInspectionFinished(this, projectScopeSelected) } catch (e: Exception) { logger.warn(e) } } } private fun pmdNodeWarmUp( scope: AnalysisScope, progressIndicator: ProgressIndicator, isOfflineInspections: Boolean ) { val aliProjectComponent = project.getComponent(AliProjectComponent::class.java) // run pmd inspection val processor = Processor { file: PsiFile -> ProgressManager.checkCanceled() val readActionSuccess = DumbService.getInstance(project).tryRunReadActionInSmartMode( { if (!file.isValid) { return@tryRunReadActionInSmartMode true } val virtualFile = file.virtualFile if (!scope.contains(virtualFile)) { logger.info(file.name + "; scope: " + scope + "; " + virtualFile) return@tryRunReadActionInSmartMode true } val path = virtualFile.canonicalPath?.toLowerCase() ?: "" if (!path.endsWith(".java") && !path.endsWith(".vm")) { return@tryRunReadActionInSmartMode true } doPmdProcess(file, aliProjectComponent, virtualFile) true }, "Inspect code is not available until indices are ready" ) if (readActionSuccess == null || !readActionSuccess) { throw ProcessCanceledException() } true } val headlessEnvironment = ApplicationManager.getApplication().isHeadlessEnvironment val searchScope = ApplicationManager.getApplication().runReadAction { scope.toSearchScope() } val localScopeFiles: MutableSet? = if (searchScope is LocalSearchScope) THashSet() else null val filesToInspect: BlockingQueue = ArrayBlockingQueue(1000) val iteratingIndicator: ProgressIndicator = SensitiveProgressWrapper(progressIndicator) val future: Future<*> = startIterateScopeInBackground( scope, localScopeFiles, headlessEnvironment, filesToInspect, iteratingIndicator ) as Future<*> val dependentIndicators = ReflectionUtil.getField(javaClass, this, List::class.java, "dependentIndicators")?.map { it as ProgressIndicator }?.toMutableList() try { val filesFailedToInspect: Queue = LinkedBlockingQueue() while (true) { val disposable = Disposer.newDisposable() val wrapper: ProgressIndicator = DaemonProgressIndicator() dependentIndicators?.let { it.add(wrapper) } try { // avoid "attach listener"/"write action" race ApplicationManager.getApplication().runReadAction { wrapper.start() ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable) // there is a chance we are racing with write action, in which case just registered listener might not be called, retry. if (ApplicationManagerEx.getApplicationEx().isWriteActionPending) { throw ProcessCanceledException() } } // use wrapper here to cancel early when write action start but do not affect the original indicator (JobLauncher.getInstance() as JobLauncherImpl).processQueue( filesToInspect, filesFailedToInspect, wrapper, PsiUtilCore.NULL_PSI_FILE, processor ) break } catch (e: ProcessCanceledException) { progressIndicator.checkCanceled() assert( isOfflineInspections || !ApplicationManager.getApplication().isReadAccessAllowed ) { """ Must be outside read action. PCE= ${ExceptionUtil.getThrowableText(e)} """.trimIndent() } assert( isOfflineInspections || !ApplicationManager.getApplication().isDispatchThread ) { """ Must be outside EDT. PCE= ${ExceptionUtil.getThrowableText(e)} """.trimIndent() } // wait for write action to complete ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance()) } finally { dependentIndicators?.let { it.remove(wrapper) } Disposer.dispose(disposable) } } } finally { iteratingIndicator.cancel() // tell file scanning thread to stop filesToInspect.clear() // let file scanning thread a chance to put TOMBSTONE and complete try { future[30, SECONDS] } catch (e: java.lang.Exception) { logger.error( """ Thread dump: ${ThreadDumper.dumpThreadsToString()} """.trimIndent(), e ) } } ProgressManager.checkCanceled() } private fun startIterateScopeInBackground( scope: AnalysisScope, localScopeFiles: MutableCollection?, headlessEnvironment: Boolean, outFilesToInspect: BlockingQueue, progressIndicator: ProgressIndicator ): Future<*>? { val task: Backgroundable = object : Backgroundable(project, "Scanning Files to Inspect") { override fun run(indicator: ProgressIndicator) { try { val fileIndex: FileIndex = ProjectRootManager.getInstance(project).fileIndex scope.accept { file: VirtualFile? -> ProgressManager.checkCanceled() if (isProjectOrWorkspaceFile(file!!) || !fileIndex.isInContent(file)) return@accept true val psiFile = ReadAction.compute { if (project.isDisposed) throw ProcessCanceledException() val psi = PsiManager.getInstance(project).findFile(file) val document = psi?.let { shouldProcess(it, headlessEnvironment, localScopeFiles) } if (document != null) { return@compute psi } null } // do not inspect binary files if (psiFile != null) { try { check(!ApplicationManager.getApplication().isReadAccessAllowed) { "Must not have read action" } outFilesToInspect.put(psiFile) } catch (e: InterruptedException) { logger.error(e) } } ProgressManager.checkCanceled() true } } catch (e: ProcessCanceledException) { // ignore, but put tombstone } finally { try { outFilesToInspect.put(PsiUtilCore.NULL_PSI_FILE) } catch (e: InterruptedException) { logger.error(e) } } } } return (ProgressManager.getInstance() as CoreProgressManager).runProcessWithProgressAsynchronously( task, progressIndicator, null ) } private fun shouldProcess( file: PsiFile, headlessEnvironment: Boolean, localScopeFiles: MutableCollection? ): Document? { val virtualFile = file.virtualFile ?: return null if (isBinary(file)) return null //do not inspect binary files if (isViewClosed && !headlessEnvironment) { throw ProcessCanceledException() } if (logger.isDebugEnabled) { logger.debug("Running local inspections on " + virtualFile.path) } if (SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile)) return null if (localScopeFiles != null && !localScopeFiles.add(virtualFile)) return null return if (!ProblemHighlightFilter.shouldProcessFileInBatch(file)) null else PsiDocumentManager.getInstance( project ).getDocument(file) } private fun isBinary(file: PsiFile): Boolean { return file is PsiBinaryFile || file.fileType.isBinary } private fun doPmdProcess( file: PsiFile, aliProjectComponent: AliProjectComponent, virtualFile: VirtualFile ) { val url: String = displayUrlRelativeToProject( virtualFile, virtualFile.presentableUrl, project, true, false ) myProgressIndicator.text = "PMD Process in $url" val violations = AliPmdProcessor(AliLocalInspectionToolProvider.getRuleSets()).processFile( file, false ) val fileContext = aliProjectComponent.getFileContext(virtualFile) fileContext?.let { fc -> val ruleViolations = ContainerUtil.createConcurrentSoftValueMap>() for (entry in violations.groupBy { it.rule.name }) { ruleViolations[entry.key] = entry.value } fc.ruleViolations = ruleViolations } } 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) } } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/SwitchLanguageAction.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { private val p3cConfig = P3cConfig::class.java.getService() private val textKey = "com.alibaba.p3c.action.switch_language.text" override fun actionPerformed(e: AnActionEvent) { p3cConfig.toggleLanguage() 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}") } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/action/ToggleProjectInspectionAction.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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") } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/InspectionProfileService.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.compatible.inspection import com.alibaba.smartfox.idea.common.util.PluginVersions import com.google.common.collect.Sets import com.intellij.codeInspection.ex.GlobalInspectionContextImpl import com.intellij.codeInspection.ex.InspectionManagerEx import com.intellij.codeInspection.ex.InspectionProfileImpl import com.intellij.codeInspection.ex.InspectionToolWrapper import com.intellij.openapi.project.Project import com.intellij.openapi.util.InvalidDataException import com.intellij.openapi.util.WriteExternalException import com.intellij.profile.codeInspection.InspectionProjectProfileManager import com.intellij.psi.PsiElement import org.jdom.Element import java.util.LinkedHashSet /** * * * @author caikang * @date 2017/03/01 */ object InspectionProfileService { fun createSimpleProfile(toolWrapperList: List>, managerEx: InspectionManagerEx, psiElement: PsiElement?): InspectionProfileImpl { val profile = getProjectInspectionProfile(managerEx.project) val allWrappers: LinkedHashSet> = Sets.newLinkedHashSet() allWrappers.addAll(toolWrapperList) val forCompile = allWrappers for (toolWrapper in allWrappers) { profile.collectDependentInspections(toolWrapper, forCompile, managerEx.project) } val model = when (PluginVersions.baseVersion) { in PluginVersions.baseVersion171..Int.MAX_VALUE -> { val clz = Class.forName("com.intellij.codeInspection.ex.InspectionProfileKt") val method = clz.methods.first { it.name == "createSimple" } method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toList()) as InspectionProfileImpl } PluginVersions.baseVersion163 -> { val method = profile.javaClass.methods.first { it.name == "createSimple" } method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toList()) as InspectionProfileImpl } else -> { val method = profile.javaClass.methods.first { it.name == "createSimple" } method.invoke(null, "Alibaba Coding Guidelines", managerEx.project, allWrappers.toTypedArray()) as InspectionProfileImpl } } try { val element = Element("toCopy") for (wrapper in allWrappers) { wrapper.tool.writeSettings(element) val tw = if (psiElement == null) { model.getInspectionTool(wrapper.shortName, managerEx.project) } else { model.getInspectionTool(wrapper.shortName, psiElement) } tw!!.tool.readSettings(element) } } catch (ignored: WriteExternalException) { } catch (ignored: InvalidDataException) { } return model } fun toggleInspection(project: Project, aliInspections: List>, closed: Boolean) { val profile = getProjectInspectionProfile(project) val shortNames = aliInspections.map { it.tool.shortName } profile.removeScopes(shortNames, "AlibabaCodeAnalysis", project) val method = profile.javaClass.methods.first { it.name == if (closed) { "enableToolsByDefault" } else { "disableToolByDefault" } } method.invoke(profile, shortNames, project) profile.profileChanged() profile.scopesChanged() } fun setExternalProfile(profile: InspectionProfileImpl, inspectionContext: GlobalInspectionContextImpl) { val method = inspectionContext.javaClass.methods.first { it.name == "setExternalProfile" && it.parameterTypes.size == 1 && it.parameterTypes.first().isAssignableFrom(InspectionProfileImpl::class.java) } method.invoke(inspectionContext, profile) } fun getProjectInspectionProfile(project: Project): InspectionProfileImpl { return InspectionProjectProfileManager.getInstance(project).inspectionProfile as InspectionProfileImpl } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/compatible/inspection/Inspections.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.google.common.base.Splitter import com.intellij.codeInspection.ex.InspectionProfileImpl import com.intellij.codeInspection.ex.InspectionToolWrapper import com.intellij.codeInspection.ex.ScopeToolState import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/03/01 */ object Inspections { fun aliInspections(project: Project, filter: (InspectionToolWrapper<*, *>) -> Boolean): List> { val profile = InspectionProfileService.getProjectInspectionProfile(project) return getAllTools(project, profile).filter(filter) } fun addCustomTag(project: Project, tag: String) { val profile = InspectionProfileService.getProjectInspectionProfile(project) val javaDocLocalInspection = profile.getInspectionTool("JavaDoc", project)?.tool as? JavaDocLocalInspection ?: return if (javaDocLocalInspection.myAdditionalJavadocTags.isEmpty()) { javaDocLocalInspection.myAdditionalJavadocTags = tag return } val tags = Splitter.on(',').splitToList(javaDocLocalInspection.myAdditionalJavadocTags) if (tags.contains(tag)) { return } javaDocLocalInspection.myAdditionalJavadocTags += "," + tag profile.profileChanged() profile.scopesChanged() } private fun getAllTools(project: Project, profile: InspectionProfileImpl): List> { val method = InspectionProfileImpl::class.java.methods.first { it.name == "getAllTools" } val result = if (method.parameterTypes.isNotEmpty()) { method.invoke(profile, project) } else { method.invoke(profile) } return (result as List).map { it.tool } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/AliProjectComponent.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.compatible.inspection.Inspections import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliPmdInspectionInvoker import com.alibaba.p3c.idea.pmd.SourceCodeProcessor import com.alibaba.p3c.idea.util.withLockNotInline import com.alibaba.p3c.pmd.I18nResources import com.alibaba.smartfox.idea.common.component.AliBaseProjectComponent import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile 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.openapi.vfs.VirtualFileMoveEvent import com.intellij.psi.PsiManager import net.sourceforge.pmd.RuleViolation import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantReadWriteLock /** * @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" private val lock = ReentrantReadWriteLock() private val readLock = lock.readLock() private val writeLock = lock.writeLock() private val fileContexts = ConcurrentHashMap() init { listener = object : VirtualFileAdapter() { override fun contentsChanged(event: VirtualFileEvent) { val path = getFilePath(event) ?: return PsiManager.getInstance(project).findFile(event.file) ?: return if (!p3cConfig.ruleCacheEnable) { AliPmdInspectionInvoker.refreshFileViolationsCache(event.file) } if (!p3cConfig.astCacheEnable) { SourceCodeProcessor.invalidateCache(path) } SourceCodeProcessor.invalidUserTrigger(path) fileContexts[path]?.ruleViolations = null } override fun fileDeleted(event: VirtualFileEvent) { val path = getFilePath(event) path?.let { SourceCodeProcessor.invalidateCache(it) removeFileContext(it) } super.fileDeleted(event) } override fun fileMoved(event: VirtualFileMoveEvent) { val path = getFilePath(event) path?.let { SourceCodeProcessor.invalidateCache(it) removeFileContext(it) } super.fileMoved(event) } private fun getFilePath(event: VirtualFileEvent): String? { val path = event.file.canonicalPath if (path == null || !(path.endsWith(javaExtension) || path.endsWith(velocityExtension))) { return null } return path } } } override fun initComponent() { I18nResources.changeLanguage(p3cConfig.locale) val analyticsGroup = ActionManager.getInstance().getAction(analyticsGroupId) analyticsGroup.templatePresentation.text = P3cBundle.getMessage(analyticsGroupText) } override fun projectOpened() { Inspections.addCustomTag(project, "date") VirtualFileManager.getInstance().addVirtualFileListener(listener) } override fun projectClosed() { VirtualFileManager.getInstance().removeVirtualFileListener(listener) } companion object { val analyticsGroupId = "com.alibaba.p3c.analytics.action_group" val analyticsGroupText = "$analyticsGroupId.text" } data class FileContext( val lock: ReentrantReadWriteLock, var ruleViolations: Map>? = null ) fun removeFileContext(path: String) { fileContexts.remove(path) } fun getFileContext(virtualFile: VirtualFile?): FileContext? { val file = virtualFile?.canonicalPath ?: return null val result = readLock.withLockNotInline { fileContexts[file] } if (result != null) { return result } return writeLock.withLockNotInline { val finalContext = fileContexts[file] if (finalContext != null) { return@withLockNotInline finalContext } val lock = ReentrantReadWriteLock() FileContext( lock = lock ).also { fileContexts[file] = it } } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/component/CommonSettingsApplicationComponent.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.HighlightInfoTypes import com.alibaba.p3c.idea.util.HighlightSeverities import com.alibaba.smartfox.idea.common.component.AliBaseApplicationComponent import com.intellij.codeInsight.daemon.impl.SeverityRegistrar /** * * * @author caikang * @date 2017/06/19 */ class CommonSettingsApplicationComponent : AliBaseApplicationComponent { override fun initComponent() { SeverityRegistrar.registerStandard(HighlightInfoTypes.BLOCKER, HighlightSeverities.BLOCKER) SeverityRegistrar.registerStandard(HighlightInfoTypes.CRITICAL, HighlightSeverities.CRITICAL) SeverityRegistrar.registerStandard(HighlightInfoTypes.MAJOR, HighlightSeverities.MAJOR) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/P3cConfig.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.xmlb.XmlSerializerUtil import java.util.Locale /** * * * @author caikang * @date 2017/06/19 */ @State(name = "P3cConfig", storages = [Storage(file = "smartfox/p3c.xml")]) class P3cConfig : PersistentStateComponent { var astCacheTime = 1000L var astCacheEnable = true var ruleCacheTime = 1000L var ruleCacheEnable = false var analysisBeforeCheckin = false var locale: String = "" get() { if (field.isEmpty()) { val lang = Locale.getDefault().language return if (lang != Locale.ENGLISH.language && lang != Locale.CHINESE.language) { Locale.ENGLISH.language } else Locale.getDefault().language } return field } fun toggleLanguage() { locale = if (localeEn == locale) localeZh else localeEn } override fun getState(): P3cConfig { return this } override fun loadState(state: P3cConfig) { XmlSerializerUtil.copyBean(state, this) } companion object { val localeEn = Locale.ENGLISH.language!! val localeZh = Locale.CHINESE.language!! } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/config/SmartFoxProjectConfig.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.xmlb.XmlSerializerUtil /** * * * @author caikang * @date 2017/03/01 */ @State(name = "SmartFoxProjectConfig", storages = [com.intellij.openapi.components.Storage(file = "smartfox_info.xml")]) class SmartFoxProjectConfig : PersistentStateComponent { var projectInspectionClosed = false override fun getState(): SmartFoxProjectConfig? { return this } override fun loadState(state: SmartFoxProjectConfig) { XmlSerializerUtil.copyBean(state, this) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/InspectionActionExtensionPoint.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.ep import com.alibaba.smartfox.idea.common.util.PluginVersions import com.intellij.codeInspection.ex.GlobalInspectionContextImpl import com.intellij.codeInspection.ui.InspectionResultsView import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/06/19 */ interface InspectionActionExtensionPoint { fun doOnInspectionFinished(context: GlobalInspectionContextImpl, projectScopeSelected: Boolean) {} fun doOnClose(noSuspiciousCodeFound: Boolean, project: Project?) {} fun doOnView(view: InspectionResultsView) {} companion object { val extension = ExtensionPointName.create( "${PluginVersions.pluginId.idString}.inspectionAction")!! } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/ep/package-info.java ================================================ /** * extension point * * @author caikang * @date 2017/06/19 */ package com.alibaba.p3c.idea.ep; ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/i18n/P3cBundle.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { private 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() } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliAccessToNonThreadSafeStaticFieldFromInstanceInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.intellij.codeHighlighting.HighlightDisplayLevel import com.siyeh.ig.threading.AccessToNonThreadSafeStaticFieldFromInstanceInspection /** * @author caikang * @date 2016/12/08 */ class AliAccessToNonThreadSafeStaticFieldFromInstanceInspection : AccessToNonThreadSafeStaticFieldFromInstanceInspection, AliBaseInspection { constructor() /** * For Javassist */ constructor(any: Any?) : this() init { nonThreadSafeClasses.clear() nonThreadSafeClasses.add("java.text.SimpleDateFormat") } override fun ruleName(): String { return "AvoidCallStaticSimpleDateFormatRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun buildErrorString(vararg infos: Any): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AvoidCallStaticSimpleDateFormatRule.errMsg") } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliArrayNamingShouldHaveBracketInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.PsiElement import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod import com.intellij.psi.PsiParameter import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.style.CStyleArrayDeclarationInspection import javax.swing.JComponent /** * * Batch QuickFix Supported * @author caikang * @date 2017/02/26 */ class AliArrayNamingShouldHaveBracketInspection : CStyleArrayDeclarationInspection, AliBaseInspection { constructor() /** * ForJavassist */ constructor(any: Any?) : this() override fun ruleName(): String { return "ArrayNamingShouldHaveBracketRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun getShortName(): String { return "AliArrayNamingShouldHaveBracket" } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } override fun createOptionsPanel(): JComponent? { return null } override fun buildErrorString(vararg infos: Any?): String { val info = infos[0] if (info is PsiMethod) { return displayName } val choice = if (info is PsiField) 1 else if (info is PsiParameter) 2 else 3 return P3cBundle.message("com.alibaba.p3c.idea.inspection.rule.ArrayNamingShouldHaveBracketRule.errMsg", choice) } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix = super.buildFix(*infos) ?: return null return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.ArrayNamingShouldHaveBracketRule")) } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { return buildFix(psiElement) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliBaseInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile /** * @author caikang * @date 2016/12/08 */ interface AliBaseInspection { /** * ruleName * @return ruleName */ fun ruleName(): String /** * display info for inspection * @return display */ fun getDisplayName(): String /** * group display info for inspection * @return group display */ fun getGroupDisplayName(): String /** * inspection enable by default * @return true -> enable */ fun isEnabledByDefault(): Boolean /** * default inspection level * @return level */ fun getDefaultLevel(): HighlightDisplayLevel /** * inspection short name * @return shor name */ fun getShortName(): String fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? = null fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, start: Int, end: Int): PsiElement { return psiFile.findElementAt(start)!! } companion object { val GROUP_NAME = "Ali-Check" } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliControlFlowStatementWithoutBracesInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.PsiElement import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.style.ControlFlowStatementWithoutBracesInspection /** * Batch QuickFix Supported * @author caikang * @date 2016/12/15 */ class AliControlFlowStatementWithoutBracesInspection : ControlFlowStatementWithoutBracesInspection, AliBaseInspection { constructor() /** * For Javassist */ constructor(any: Any?) : this() override fun ruleName(): String { return "NeedBraceRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun buildErrorString(vararg infos: Any): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.NeedBraceRule.errMsg") } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix = super.buildFix(*infos) ?: return null return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.NeedBraceRule")) } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { return buildFix(psiElement) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliEqualsAvoidNullInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.PsiElement import com.intellij.psi.PsiExpression import com.intellij.psi.PsiField import com.intellij.psi.PsiFile import com.intellij.psi.PsiLiteralExpression import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiReferenceExpression import com.siyeh.HardcodedMethodConstants import com.siyeh.ig.BaseInspectionVisitor import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.psiutils.TypeUtils import com.siyeh.ig.style.LiteralAsArgToStringEqualsInspection import org.jetbrains.annotations.NonNls /** * * Batch QuickFix Supported * @author caikang * @date 2017/02/27 */ class AliEqualsAvoidNullInspection : LiteralAsArgToStringEqualsInspection, AliBaseInspection { constructor() /** * For Javassist */ constructor(any: Any?) : this() override fun ruleName(): String { return "EqualsAvoidNullRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun buildErrorString(vararg infos: Any?): String { val methodName = infos[0] as String return String.format(P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AliEqualsAvoidNull.errMsg"), methodName) } override fun getShortName(): String { return "AliEqualsAvoidNull" } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } override fun buildVisitor(): BaseInspectionVisitor { return LiteralAsArgToEqualsVisitor() } private class LiteralAsArgToEqualsVisitor : BaseInspectionVisitor() { override fun visitMethodCallExpression( expression: PsiMethodCallExpression) { super.visitMethodCallExpression(expression) val methodExpression = expression.methodExpression @NonNls val methodName = methodExpression.referenceName if (HardcodedMethodConstants.EQUALS != methodName && HardcodedMethodConstants.EQUALS_IGNORE_CASE != methodName) { return } val argList = expression.argumentList val args = argList.expressions if (args.size != 1) { return } val argument = args[0] val argumentType = argument.type ?: return if (argument !is PsiLiteralExpression && !isConstantField(argument)) { return } if (!TypeUtils.isJavaLangString(argumentType)) { return } val target = methodExpression.qualifierExpression if (target is PsiLiteralExpression || isConstantField(argument)) { return } registerError(argument, methodName) } private fun isConstantField(argument: PsiExpression): Boolean { if (argument !is PsiReferenceExpression) { return false } val psiField = argument.resolve() as? PsiField ?: return false val modifierList = psiField.modifierList ?: return false return modifierList.hasModifierProperty("final") && modifierList.hasModifierProperty("static") } } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix = super.buildFix(*infos) ?: return null return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.AliEqualsAvoidNull")) } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { return buildFix(psiElement) } override fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, start: Int, end: Int): PsiElement { return psiFile.findElementAt(start)!!.parent.parent } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLocalInspectionToolProvider.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.inspection.standalone.AliAccessStaticViaInstanceInspection import com.alibaba.p3c.idea.inspection.standalone.AliDeprecationInspection import com.alibaba.p3c.idea.inspection.standalone.AliMissingOverrideAnnotationInspection import com.alibaba.p3c.idea.inspection.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsInspection import com.alibaba.p3c.pmd.I18nResources import com.alibaba.smartfox.idea.common.util.getService import com.beust.jcommander.internal.Lists import com.beust.jcommander.internal.Maps import com.intellij.codeInspection.InspectionToolProvider import com.intellij.codeInspection.LocalInspectionTool import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.roots.ProjectRootManager import com.intellij.psi.PsiCompiledFile import com.intellij.psi.PsiFile import com.intellij.psi.PsiImportList import com.intellij.psi.PsiJavaFile import javassist.CannotCompileException import javassist.ClassClassPath import javassist.ClassPool import javassist.CtField import javassist.NotFoundException import net.sourceforge.pmd.Rule import net.sourceforge.pmd.RuleSet import net.sourceforge.pmd.RuleSetFactory import net.sourceforge.pmd.RuleSetNotFoundException import net.sourceforge.pmd.RuleSets import javax.annotation.Generated /** * @author caikang * @date 2016/12/16 */ class AliLocalInspectionToolProvider : InspectionToolProvider { override fun getInspectionClasses(): Array> { return CLASS_LIST.toTypedArray() } interface ShouldInspectChecker { /** * check inspect whether or not * @param file file to inspect * @return true or false */ fun shouldInspect(file: PsiFile): Boolean } class RuleInfo(var rule: Rule, var shouldInspectChecker: ShouldInspectChecker) companion object { val ruleInfoMap: MutableMap = Maps.newHashMap() private val LOGGER = Logger.getInstance(AliLocalInspectionToolProvider::class.java) val ruleNames: MutableList = Lists.newArrayList()!! private val CLASS_LIST = Lists.newArrayList>() private val nativeInspectionToolClass = arrayListOf>( AliMissingOverrideAnnotationInspection::class.java, AliAccessStaticViaInstanceInspection::class.java, AliDeprecationInspection::class.java, MapOrSetKeyShouldOverrideHashCodeEqualsInspection::class.java, AliArrayNamingShouldHaveBracketInspection::class.java, AliControlFlowStatementWithoutBracesInspection::class.java, AliEqualsAvoidNullInspection::class.java, AliLongLiteralsEndingWithLowercaseLInspection::class.java, AliWrapperTypeEqualityInspection::class.java ) val javaShouldInspectChecker = object : ShouldInspectChecker { override fun shouldInspect(file: PsiFile): Boolean { val basicInspect = file is PsiJavaFile && file !is PsiCompiledFile if (!basicInspect) { return false } if (!validScope(file)) { return false } val importList = file.children.firstOrNull { it is PsiImportList } as? PsiImportList ?: return true return !importList.allImportStatements.any { it.text.contains(Generated::class.java.name) } } private fun validScope(file: PsiFile): Boolean { val virtualFile = file.virtualFile val index = ProjectRootManager.getInstance(file.project).fileIndex return index.isInSource(virtualFile) && !index.isInTestSourceContent(virtualFile) && !index.isInLibraryClasses(virtualFile) && !index.isInLibrarySource(virtualFile) } } init { I18nResources.changeLanguage(P3cConfig::class.java.getService().locale) Thread.currentThread().contextClassLoader = AliLocalInspectionToolProvider::class.java.classLoader initPmdInspection() initNativeInspection() } private fun initNativeInspection() { val pool = ClassPool.getDefault() pool.insertClassPath(ClassClassPath(DelegateLocalInspectionTool::class.java)) nativeInspectionToolClass.forEach { pool.insertClassPath(ClassClassPath(it)) val cc = pool.get(DelegateLocalInspectionTool::class.java.name) cc.name = "Delegate${it.simpleName}" val ctField = cc.getField("forJavassist") cc.removeField(ctField) val itClass = pool.get(it.name) val toolClass = pool.get(LocalInspectionTool::class.java.name) val newField = CtField(toolClass, "forJavassist", cc) cc.addField(newField, CtField.Initializer.byNew(itClass)) CLASS_LIST.add(cc.toClass() as Class) } } private fun initPmdInspection() { for (ri in newRuleInfos()) { this.ruleNames.add(ri.rule.name) ruleInfoMap.put(ri.rule.name, ri) } val pool = ClassPool.getDefault() pool.insertClassPath(ClassClassPath(DelegatePmdInspection::class.java)) try { for (ruleInfo in ruleInfoMap.values) { val cc = pool.get(DelegatePmdInspection::class.java.name) cc.name = ruleInfo.rule.name + "Inspection" val ctField = cc.getField("ruleName") cc.removeField(ctField) val value = "\"" + ruleInfo.rule.name + "\"" val newField = CtField.make("private String ruleName = $value;", cc) cc.addField(newField, value) CLASS_LIST.add(cc.toClass() as Class) } } catch (e: NotFoundException) { LOGGER.error(e) } catch (e: CannotCompileException) { LOGGER.error(e) } } private fun newRuleInfos(): List { val result = Lists.newArrayList() result.addAll(processForRuleSet("java/ali-pmd", javaShouldInspectChecker)) result.addAll(processForRuleSet("vm/ali-other", object : ShouldInspectChecker { override fun shouldInspect(file: PsiFile): Boolean { val virtualFile = file.virtualFile ?: return false val path = virtualFile.canonicalPath return path != null && path.endsWith(".vm") } })) return result } fun getRuleSet(ruleSetName: String): RuleSet { val factory = RuleSetFactory() return factory.createRuleSet(ruleSetName.replace("/", "-")) } fun getRuleSetList(): List { return listOf(getRuleSet("java/ali-pmd"), getRuleSet("vm/ali-other")) } fun getRuleSets(): RuleSets { return RuleSets().also { rs -> for (ruleSet in getRuleSetList()) { rs.addRuleSet(ruleSet) } } } private fun processForRuleSet(ruleSetName: String, shouldInspectChecker: ShouldInspectChecker): List { val result = Lists.newArrayList() try { val ruleSet = getRuleSet(ruleSetName) ruleSet.rules.mapTo(result) { RuleInfo(it, shouldInspectChecker) } } catch (e: RuleSetNotFoundException) { LOGGER.error(String.format("rule set %s not found for", ruleSetName)) } return result } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliLongLiteralsEndingWithLowercaseLInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.numeric.LongLiteralsEndingWithLowercaseLInspection /** * * Batch QuickFix Supported * @author caikang * @date 2017/01/20 */ class AliLongLiteralsEndingWithLowercaseLInspection : LongLiteralsEndingWithLowercaseLInspection, AliBaseInspection { constructor() /** * For Javassist */ constructor(any: Any?) : this() override fun ruleName(): String { return "UpperEllRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun buildErrorString(vararg infos: Any?): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.AliLongLiteralsEndingWithLowercaseL.errMsg") } override fun getShortName(): String { return "AliLongLiteralsEndingWithLowercaseL" } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix = super.buildFix(*infos) ?: return null return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.AliLongLiteralsEndingWithLowercaseL")) } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { return buildFix(psiElement) } override fun manualParsePsiElement(psiFile: PsiFile, manager: InspectionManager, start: Int, end: Int): PsiElement { return psiFile.findElementAt(start)!!.parent } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.inspection.AliLocalInspectionToolProvider.ShouldInspectChecker import com.alibaba.p3c.idea.util.NumberConstants import com.alibaba.p3c.idea.util.QuickFixes import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import net.sourceforge.pmd.Rule import org.jetbrains.annotations.Nls /** * @author caikang * @date 2016/12/16 */ class AliPmdInspection(private val ruleName: String) : LocalInspectionTool(), AliBaseInspection, PmdRuleInspectionIdentify { override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { return QuickFixes.getQuickFix(ruleName, isOnTheFly) } private val staticDescription: String = RuleInspectionUtils.getRuleStaticDescription(ruleName) private val displayName: String private val shouldInspectChecker: ShouldInspectChecker private val defaultLevel: HighlightDisplayLevel private val rule: Rule init { val ruleInfo = AliLocalInspectionToolProvider.ruleInfoMap[ruleName]!! shouldInspectChecker = ruleInfo.shouldInspectChecker rule = ruleInfo.rule displayName = rule.message defaultLevel = RuleInspectionUtils.getHighlightDisplayLevel(rule.priority) } override fun runForWholeFile(): Boolean { return true } override fun checkFile( file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean ): Array? { if (!shouldInspectChecker.shouldInspect(file)) { return null } return AliPmdInspectionInvoker.invokeInspection(file, manager, rule, isOnTheFly) } override fun getStaticDescription(): String? { return staticDescription } override fun ruleName(): String { return ruleName } @Nls override fun getDisplayName(): String { return displayName } override fun getDefaultLevel(): HighlightDisplayLevel { return defaultLevel } @Nls override fun getGroupDisplayName(): String { return AliBaseInspection.GROUP_NAME } override fun isEnabledByDefault(): Boolean { return true } override fun getShortName(): String { var shortName = "Alibaba" + ruleName val index = shortName.lastIndexOf("Rule") if (index > NumberConstants.INDEX_0) { shortName = shortName.substring(NumberConstants.INDEX_0, index) } return shortName } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliPmdInspectionInvoker.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.component.AliProjectComponent import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.pmd.AliPmdProcessor import com.alibaba.p3c.idea.util.DocumentUtils.calculateLineStart import com.alibaba.p3c.idea.util.DocumentUtils.calculateRealOffset import com.alibaba.p3c.idea.util.ProblemsUtils import com.alibaba.p3c.pmd.lang.java.rule.comment.RemoveCommentedCodeRule import com.beust.jcommander.internal.Lists import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import net.sourceforge.pmd.Rule import net.sourceforge.pmd.RuleViolation import java.util.concurrent.TimeUnit /** * @author caikang * @date 2016/12/13 */ class AliPmdInspectionInvoker( private val psiFile: PsiFile, private val manager: InspectionManager, private val rule: Rule ) { private val logger = Logger.getInstance(javaClass) private var violations: List = emptyList() fun doInvoke(isOnTheFly: Boolean) { Thread.currentThread().contextClassLoader = javaClass.classLoader val processor = AliPmdProcessor(rule) val start = System.currentTimeMillis() val aliProjectComponent = manager.project.getComponent(AliProjectComponent::class.java) val fileContext = aliProjectComponent.getFileContext(psiFile.virtualFile) val ruleViolations = fileContext?.ruleViolations violations = if (isOnTheFly || ruleViolations == null) { processor.processFile(psiFile, isOnTheFly) } else { ruleViolations[rule.name] ?: emptyList() } logger.debug( "elapsed ${System.currentTimeMillis() - start}ms to" + " to apply rule ${rule.name} for file ${psiFile.virtualFile.canonicalPath}" ) } fun getRuleProblems(isOnTheFly: Boolean): Array? { if (violations.isEmpty()) { return null } val problemDescriptors = Lists.newArrayList(violations.size) for (rv in violations) { val virtualFile = LocalFileSystem.getInstance().findFileByPath(rv.filename) ?: continue val psiFile = PsiManager.getInstance(manager.project).findFile(virtualFile) ?: continue val document = FileDocumentManager.getInstance().getDocument(virtualFile) ?: continue val offsets = if (rv.rule.name == RemoveCommentedCodeRule::class.java.simpleName) { Offsets( calculateLineStart(document, rv.beginLine), calculateLineStart(document, rv.endLine + 1) - 1 ) } else { Offsets( calculateRealOffset(document, rv.beginLine, rv.beginColumn), calculateRealOffset(document, rv.endLine, rv.endColumn) ) } val errorMessage = if (isOnTheFly) { rv.description } else { "${rv.description} (line ${rv.beginLine})" } val problemDescriptor = ProblemsUtils.createProblemDescriptorForPmdRule( psiFile, manager, isOnTheFly, rv.rule.name, errorMessage, offsets.start, offsets.end, rv.beginLine ) ?: continue problemDescriptors.add(problemDescriptor) } return problemDescriptors.toTypedArray() } companion object { private lateinit var invokers: Cache val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!! init { reInitInvokers(smartFoxConfig.ruleCacheTime) } fun invokeInspection( psiFile: PsiFile?, manager: InspectionManager, rule: Rule, isOnTheFly: Boolean ): Array? { if (psiFile == null) { return null } val virtualFile = psiFile.virtualFile ?: return null if (!smartFoxConfig.ruleCacheEnable) { val invoker = AliPmdInspectionInvoker(psiFile, manager, rule) invoker.doInvoke(isOnTheFly) return invoker.getRuleProblems(isOnTheFly) } var invoker = invokers.getIfPresent(FileRule(virtualFile.canonicalPath!!, rule.name)) if (invoker == null) { synchronized(virtualFile) { invoker = invokers.getIfPresent(virtualFile.canonicalPath!!) if (invoker == null) { invoker = AliPmdInspectionInvoker(psiFile, manager, rule) invoker!!.doInvoke(isOnTheFly) invokers.put(FileRule(virtualFile.canonicalPath!!, rule.name), invoker!!) } } } return invoker!!.getRuleProblems(isOnTheFly) } private fun doInvokeIfPresent(filePath: String, rule: String, isOnTheFly: Boolean) { invokers.getIfPresent(FileRule(filePath, rule))?.doInvoke(isOnTheFly) } fun refreshFileViolationsCache(file: VirtualFile) { AliLocalInspectionToolProvider.ruleNames.forEach { doInvokeIfPresent(file.canonicalPath!!, it, false) } } fun reInitInvokers(expireTime: Long) { invokers = CacheBuilder.newBuilder().maximumSize(500).expireAfterWrite( expireTime, TimeUnit.MILLISECONDS ).build()!! } } } data class FileRule(val filePath: String, val ruleName: String) data class Offsets(val start: Int, val end: Int) ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/AliWrapperTypeEqualityInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.intellij.psi.CommonClassNames import com.intellij.psi.JavaTokenType import com.intellij.psi.PsiArrayType import com.intellij.psi.PsiBinaryExpression import com.intellij.psi.PsiElement import com.intellij.psi.PsiExpression import com.intellij.util.IncorrectOperationException import com.siyeh.ig.BaseInspection import com.siyeh.ig.BaseInspectionVisitor import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.PsiReplacementUtil import com.siyeh.ig.fixes.EqualityToEqualsFix import com.siyeh.ig.psiutils.ComparisonUtils import com.siyeh.ig.psiutils.TypeUtils import org.jetbrains.annotations.NonNls /** * * Batch QuickFix Supported * @author caikang * @date 2017/02/27 */ class AliWrapperTypeEqualityInspection : BaseInspection, AliBaseInspection { constructor() /** * For Javassist */ constructor(any: Any?) : this() val familyName = "$replaceWith equals" override fun buildErrorString(vararg infos: Any?): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.inspection.rule.WrapperTypeEqualityRule.errMsg") } override fun buildVisitor(): BaseInspectionVisitor { return ObjectComparisonVisitor() } override fun ruleName(): String { return "WrapperTypeEqualityRule" } override fun getDisplayName(): String { return RuleInspectionUtils.getRuleMessage(ruleName()) } override fun getShortName(): String { return "AliWrapperTypeEquality" } override fun getStaticDescription(): String? { return RuleInspectionUtils.getRuleStaticDescription(ruleName()) } override fun getDefaultLevel(): HighlightDisplayLevel { return RuleInspectionUtils.getHighlightDisplayLevel(ruleName()) } public override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { if (infos.isEmpty()) { return DecorateInspectionGadgetsFix(EqualityToEqualsFix(), familyName) } val type = infos[0] as PsiArrayType val componentType = type.componentType val fix = ArrayEqualityFix(componentType is PsiArrayType, familyName) return DecorateInspectionGadgetsFix(fix, fix.name, familyName) } private inner class ObjectComparisonVisitor : BaseInspectionVisitor() { override fun visitBinaryExpression(expression: PsiBinaryExpression) { if (!ComparisonUtils.isEqualityComparison(expression)) { return } checkForWrapper(expression) } private fun checkForWrapper(expression: PsiBinaryExpression) { val rhs = expression.rOperand ?: return val lhs = expression.lOperand if (!isWrapperType(lhs) || !isWrapperType(rhs)) { return } registerError(expression.operationSign) } private fun isWrapperType(expression: PsiExpression): Boolean { if (hasNumberType(expression)) { return true } return TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_BOOLEAN) || TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_CHARACTER) } private fun hasNumberType(expression: PsiExpression): Boolean { return TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_NUMBER) } /** * checkForNumber end */ } private class ArrayEqualityFix(private val deepEquals: Boolean, private val familyName: String) : InspectionGadgetsFix() { override fun getName(): String { if (deepEquals) { return "$replaceWith 'Arrays.deepEquals()'" } else { return "$replaceWith 'Arrays.equals()'" } } override fun getFamilyName(): String { return familyName } @Throws(IncorrectOperationException::class) override fun doFix(project: Project, descriptor: ProblemDescriptor) { val element = descriptor.psiElement val parent = element.parent as? PsiBinaryExpression ?: return val tokenType = parent.operationTokenType @NonNls val newExpressionText = StringBuilder() if (JavaTokenType.NE == tokenType) { newExpressionText.append('!') } else if (JavaTokenType.EQEQ != tokenType) { return } if (deepEquals) { newExpressionText.append("java.util.Arrays.deepEquals(") } else { newExpressionText.append("java.util.Arrays.equals(") } newExpressionText.append(parent.lOperand.text) newExpressionText.append(',') val rhs = parent.rOperand ?: return newExpressionText.append(rhs.text) newExpressionText.append(')') PsiReplacementUtil.replaceExpressionAndShorten( parent, newExpressionText.toString() ) } } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? { val expression = psiElement.parent as? PsiBinaryExpression ?: return null val rhs = expression.rOperand ?: return null val lhs = expression.lOperand val lhsType = lhs.type if (lhsType !is PsiArrayType || rhs.type !is PsiArrayType) { return buildFix() } return buildFix(lhsType) } companion object { val replaceWith = P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.replace.with") } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegateLocalInspectionTool.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiFile import org.jetbrains.annotations.Nls /** * * @author caikang * @date 2017/07/19 */ class DelegateLocalInspectionTool : LocalInspectionTool(), AliBaseInspection { private val forJavassist: LocalInspectionTool? = null private val localInspectionTool: LocalInspectionTool init { localInspectionTool = forJavassist ?: throw IllegalStateException() } override fun runForWholeFile(): Boolean { return localInspectionTool.runForWholeFile() } override fun checkFile( file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean ): Array? { return localInspectionTool.checkFile(file, manager, isOnTheFly) } override fun getStaticDescription(): String? { return localInspectionTool.staticDescription } override fun ruleName(): String { return (localInspectionTool as AliBaseInspection).ruleName() } @Nls override fun getDisplayName(): String { return localInspectionTool.displayName } override fun getDefaultLevel(): HighlightDisplayLevel { return localInspectionTool.defaultLevel } @Nls override fun getGroupDisplayName(): String { return AliBaseInspection.GROUP_NAME } override fun isEnabledByDefault(): Boolean { return true } override fun getShortName(): String { return localInspectionTool.shortName } override fun isSuppressedFor(element: PsiElement): Boolean { return localInspectionTool.isSuppressedFor(element) } override fun buildVisitor( holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession ): PsiElementVisitor { if (!AliLocalInspectionToolProvider.javaShouldInspectChecker.shouldInspect(holder.file)) { return PsiElementVisitor.EMPTY_VISITOR } return localInspectionTool.buildVisitor(holder, isOnTheFly, session) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/DelegatePmdInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.ProblemDescriptor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import org.jetbrains.annotations.Nls /** * @author caikang * @date 2017/02/28 */ class DelegatePmdInspection : LocalInspectionTool(), AliBaseInspection, PmdRuleInspectionIdentify { private val ruleName: String? = null private val aliPmdInspection: AliPmdInspection init { aliPmdInspection = AliPmdInspection(ruleName!!) } override fun runForWholeFile(): Boolean { return aliPmdInspection.runForWholeFile() } override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array? { return aliPmdInspection.checkFile(file, manager, isOnTheFly) } override fun getStaticDescription(): String? { return aliPmdInspection.staticDescription } override fun ruleName(): String { return ruleName!! } @Nls override fun getDisplayName(): String { return aliPmdInspection.displayName } override fun getDefaultLevel(): HighlightDisplayLevel { return aliPmdInspection.defaultLevel } @Nls override fun getGroupDisplayName(): String { return aliPmdInspection.groupDisplayName } override fun isEnabledByDefault(): Boolean { return aliPmdInspection.isEnabledByDefault } override fun getShortName(): String { return aliPmdInspection.shortName } override fun isSuppressedFor(element: PsiElement): Boolean { return aliPmdInspection.isSuppressedFor(element) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/PmdRuleInspectionIdentify.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection /** * * * @author caikang * @date 2017/03/16 6 */ interface PmdRuleInspectionIdentify { } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/RuleInspectionUtils.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.util.HighlightDisplayLevels import com.alibaba.p3c.idea.util.NumberConstants import com.alibaba.p3c.pmd.I18nResources import com.alibaba.smartfox.idea.common.util.getService import com.google.common.base.Joiner import com.google.common.collect.ImmutableMap import com.google.common.collect.Lists import com.google.common.collect.Maps import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.io.FileUtil import com.intellij.util.io.URLUtil import freemarker.template.Configuration import freemarker.template.TemplateException import net.sourceforge.pmd.Rule import net.sourceforge.pmd.RulePriority import net.sourceforge.pmd.RuleSetFactory import net.sourceforge.pmd.RuleSetNotFoundException import org.apache.commons.lang3.StringUtils import java.io.File import java.io.IOException import java.io.StringWriter import java.net.URL import java.net.URLDecoder import java.nio.charset.StandardCharsets import java.util.jar.JarFile import java.util.regex.Pattern /** * @author caikang * @date 2016/12/16 */ object RuleInspectionUtils { private val logger = Logger.getInstance(RuleInspectionUtils::class.java) private val ruleSetFilePattern = Pattern.compile("(java|vm)/ali-.*?\\.xml") private val staticDescriptionTemplate = run { val cfg = Configuration(Configuration.VERSION_2_3_25) cfg.setClassForTemplateLoading(RuleInspectionUtils::class.java, "/tpl") cfg.defaultEncoding = "UTF-8" cfg.getTemplate("StaticDescriptionTemplate.ftl") } private val ruleSetsPrefix = "rulesets/" private val ruleStaticDescriptions: Map private val ruleMessages: Map private val displayLevelMap: Map init { I18nResources.changeLanguage(P3cConfig::class.java.getService().locale) val builder = ImmutableMap.builder() val messageBuilder = ImmutableMap.builder() val displayLevelBuilder = ImmutableMap.builder() val rules = loadAllAlibabaRule() for (rule in rules) { builder.put(rule.name, parseStaticDescription(rule)) messageBuilder.put(rule.name, rule.message) displayLevelBuilder.put(rule.name, getHighlightDisplayLevel(rule.priority)) } ruleStaticDescriptions = builder.build() ruleMessages = messageBuilder.build() displayLevelMap = displayLevelBuilder.build() } fun getRuleStaticDescription(ruleName: String): String { return ruleStaticDescriptions[ruleName]!! } fun getHighlightDisplayLevel(ruleName: String): HighlightDisplayLevel { val level = displayLevelMap[ruleName] return level ?: HighlightDisplayLevel.WEAK_WARNING } fun getHighlightDisplayLevel(rulePriority: RulePriority): HighlightDisplayLevel { when (rulePriority) { RulePriority.HIGH -> return HighlightDisplayLevels.BLOCKER RulePriority.MEDIUM_HIGH -> return HighlightDisplayLevels.CRITICAL else -> return HighlightDisplayLevels.MAJOR } } fun getRuleMessage(ruleName: String): String { return ruleMessages[ruleName]!! } private fun parseStaticDescription(rule: Rule): String { val writer = StringWriter() try { val map = Maps.newHashMap() map.put("message", StringUtils.trimToEmpty(rule.message)) map.put("description", StringUtils.trimToEmpty(rule.description)) val examples = rule.examples.map { it?.trim { c -> c == '\n' } } map.put("examples", examples) staticDescriptionTemplate.process(map, writer) } catch (e: TemplateException) { logger.error(e) } catch (e: IOException) { logger.error(e) } return writer.toString() } private fun loadAllAlibabaRule(): List { try { Thread.currentThread().contextClassLoader = RuleInspectionUtils::class.java.classLoader val ruleSetConfigs = findRuleSetConfigs() val ruleSetFactory = RuleSetFactory() val ruleSets = ruleSetFactory.createRuleSets( Joiner.on(",").join(ruleSetConfigs).replace("/".toRegex(), "-")) val map = Maps.newHashMap() ruleSets.allRuleSets .asSequence() .flatMap { it.rules.asSequence() } .forEach { map.put(it.name, it) } return Lists.newArrayList(map.values) } catch (e: IOException) { logger.warn("no available alibaba rules") return emptyList() } catch (e: RuleSetNotFoundException) { logger.error("rule sets not found", e) return emptyList() } } @Throws(IOException::class) private fun findRuleSetConfigs(): List { val ruleSets = Lists.newArrayList() val enumeration = RuleInspectionUtils::class.java.classLoader.getResources(ruleSetsPrefix) while (enumeration.hasMoreElements()) { val url = enumeration.nextElement() if (URLUtil.JAR_PROTOCOL == url.protocol) { findRuleSetsFromJar(ruleSets, url) } else if (URLUtil.FILE_PROTOCOL == url.protocol) { findRuleSetsFromDirectory(ruleSets, url) } } return ruleSets } @Throws(IOException::class) private fun findRuleSetsFromDirectory(ruleSets: MutableList, url: URL) { val file = File(url.path) if (file.exists() && file.isDirectory) { val files = Lists.newArrayList() FileUtil.collectMatchedFiles(file, ruleSetFilePattern, files) files.mapTo(ruleSets) { it.canonicalPath.replace(url.path, "").replace(".xml", "") } } } @Throws(IOException::class) private fun findRuleSetsFromJar(ruleSets: MutableList, url: URL) { logger.info("start to find rule sets from jar " + url) var path = URLDecoder.decode(url.path, StandardCharsets.UTF_8.name()) val index = path.lastIndexOf(URLUtil.JAR_SEPARATOR) if (index > NumberConstants.INDEX_0) { path = path.substring("file:".length, index) } val jarFile = JarFile(path) logger.info("create jarFile for path " + path) val jarEntries = jarFile.entries() while (jarEntries.hasMoreElements()) { val jarEntry = jarEntries.nextElement() val subPath = jarEntry.name.replace(ruleSetsPrefix, "") if (ruleSetFilePattern.matcher(subPath).find()) { val resultPath = subPath.replace(".xml", "") logger.info("get result rule set " + resultPath) ruleSets.add(resultPath) } } logger.info("find rule sets from jar $url finished") } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliAccessStaticViaInstanceInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection.standalone import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.p3c.idea.util.HighlightDisplayLevels import com.alibaba.smartfox.idea.common.util.getService import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil import com.intellij.codeInsight.daemon.impl.quickfix.AccessStaticViaInstanceFix import com.intellij.codeInsight.daemon.impl.quickfix.RemoveUnusedVariableUtil import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance import com.intellij.psi.JavaElementVisitor import com.intellij.psi.JavaResolveResult import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiMember import com.intellij.psi.PsiModifier import com.intellij.psi.PsiPackage import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.PsiSubstitutor import java.util.ArrayList /** * @author caikang * @date 2016/12/08 */ class AliAccessStaticViaInstanceInspection : AccessStaticViaInstance, AliBaseInspection { val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliAccessStaticViaInstanceInspection" constructor() /** * For Javassist */ constructor(any: Any?) : this() override fun getDisplayName(): String { return P3cBundle.getMessage("$messageKey.message") } override fun getStaticDescription(): String? { return P3cBundle.getMessage("$messageKey.desc") } override fun ruleName(): String { return "AvoidAccessStaticViaInstanceRule" } override fun getShortName(): String { return "AliAccessStaticViaInstance" } override fun createAccessStaticViaInstanceFix(expr: PsiReferenceExpression, onTheFly: Boolean, result: JavaResolveResult): AccessStaticViaInstanceFix { return object : AccessStaticViaInstanceFix(expr, result, onTheFly) { val fixKey = "com.alibaba.p3c.idea.quickfix.standalone.AliAccessStaticViaInstanceInspection" internal val text = calcText(result.element as PsiMember, result.substitutor) override fun getText(): String { return text } private fun calcText(member: PsiMember, substitutor: PsiSubstitutor): String { val aClass = member.containingClass ?: return "" val p3cConfig = P3cConfig::class.java.getService() return when (p3cConfig.locale) { P3cConfig.localeZh -> String.format(P3cBundle.getMessage(fixKey), HighlightUtil.formatClass(aClass, false), HighlightUtil.formatClass(aClass), HighlightMessageUtil.getSymbolName(member, substitutor)) else -> String.format(P3cBundle.getMessage(fixKey), HighlightUtil.formatClass(aClass), HighlightMessageUtil.getSymbolName(member, substitutor), HighlightUtil.formatClass(aClass, false)) } } } } override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { return object : JavaElementVisitor() { override fun visitReferenceExpression(expression: PsiReferenceExpression) { checkAccessStaticMemberViaInstanceReference(expression, holder, isOnTheFly) } } } override fun getDefaultLevel(): HighlightDisplayLevel { return HighlightDisplayLevels.BLOCKER } private fun checkAccessStaticMemberViaInstanceReference(expr: PsiReferenceExpression, holder: ProblemsHolder, onTheFly: Boolean) { val result = expr.advancedResolve(false) val resolved = result.element as? PsiMember ?: return val qualifierExpression = expr.qualifierExpression ?: return if (qualifierExpression is PsiReferenceExpression) { val qualifierResolved = qualifierExpression.resolve() if (qualifierResolved is PsiClass || qualifierResolved is PsiPackage) { return } } if (!resolved.hasModifierProperty(PsiModifier.STATIC)) { return } val description = String.format(P3cBundle.getMessage( "$messageKey.errMsg"), "${JavaHighlightUtil.formatType(qualifierExpression.type)}.${HighlightMessageUtil.getSymbolName( resolved, result.substitutor)}") if (!onTheFly) { if (RemoveUnusedVariableUtil.checkSideEffects(qualifierExpression, null, ArrayList())) { holder.registerProblem(expr, description) return } } holder.registerProblem(expr, description, createAccessStaticViaInstanceFix(expr, onTheFly, result)) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliDeprecationInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection.standalone import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.p3c.idea.util.HighlightDisplayLevels import com.alibaba.smartfox.idea.common.util.getService import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.deprecation.DeprecationInspection import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiReference import org.jetbrains.annotations.Nls import javax.swing.JComponent /** * @author caikang * @date 2016/12/08 */ class AliDeprecationInspection : DeprecationInspection, AliBaseInspection { private val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliDeprecationInspection" constructor() /** * For Javassist */ constructor(any: Any?) : this() init { IGNORE_INSIDE_DEPRECATED = true IGNORE_ABSTRACT_DEPRECATED_OVERRIDES = false IGNORE_IMPORT_STATEMENTS = false IGNORE_METHODS_OF_DEPRECATED = false } override fun getDisplayName(): String { return P3cBundle.getMessage("$messageKey.message") } override fun ruleName(): String { return "AvoidUseDeprecationApiRule" } override fun getShortName(): String { return "AliDeprecation" } override fun getStaticDescription(): String? { return P3cBundle.getMessage("$messageKey.desc") } override fun createOptionsPanel(): JComponent? { return null } override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { val p3cConfig = P3cConfig::class.java.getService() return when (p3cConfig.locale) { P3cConfig.localeEn -> super.buildVisitor(holder, isOnTheFly) else -> super.buildVisitor(DeprecationInspectionProblemsHolder(holder, isOnTheFly), isOnTheFly) } } override fun getDefaultLevel(): HighlightDisplayLevel { return HighlightDisplayLevels.CRITICAL } class DeprecationInspectionProblemsHolder(private val holder: ProblemsHolder, onTheFly: Boolean) : ProblemsHolder( holder.manager, holder.file, onTheFly) { override fun registerProblem(psiElement: PsiElement, @Nls descriptionTemplate: String, fixes: Array?) { holder.registerProblem(psiElement, getMessage(descriptionTemplate), *(fixes ?: emptyArray())) } override fun registerProblem(psiElement: PsiElement, @Nls descriptionTemplate: String, highlightType: ProblemHighlightType, fixes: Array?) { holder.registerProblem(psiElement, getMessage(descriptionTemplate), highlightType, *(fixes ?: emptyArray())) } override fun registerProblem(reference: PsiReference, descriptionTemplate: String, highlightType: ProblemHighlightType) { holder.registerProblem(reference, getMessage(descriptionTemplate), highlightType) } override fun registerProblemForReference(reference: PsiReference, highlightType: ProblemHighlightType, descriptionTemplate: String, fixes: Array?) { holder.registerProblemForReference(reference, highlightType, getMessage(descriptionTemplate), *(fixes ?: emptyArray())) } override fun registerProblem(psiElement: PsiElement, rangeInElement: TextRange?, message: String, fixes: Array?) { holder.registerProblem(psiElement, rangeInElement, getMessage(message), *(fixes ?: emptyArray())) } override fun registerProblem(psiElement: PsiElement, message: String, highlightType: ProblemHighlightType, rangeInElement: TextRange?, fixes: Array?) { holder.registerProblem(psiElement, getMessage(message), highlightType, rangeInElement, *(fixes ?: emptyArray())) } private fun getMessage(msg: String): String { return msg.replace("is deprecated", "已经过时了").replace("Default constructor in", "默认构造函数") .replace("Overrides deprecated method in", "重写了过时的方法") + " #loc" } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/AliMissingOverrideAnnotationInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection.standalone import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.p3c.idea.quickfix.DecorateInspectionGadgetsFix import com.alibaba.p3c.idea.util.HighlightDisplayLevels import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.LocalQuickFix import com.intellij.psi.CommonClassNames import com.intellij.psi.PsiAnonymousClass import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifierListOwner import com.intellij.psi.util.InheritanceUtil import com.siyeh.ig.BaseInspectionVisitor import com.siyeh.ig.InspectionGadgetsFix import com.siyeh.ig.inheritance.MissingOverrideAnnotationInspection import com.siyeh.ig.psiutils.MethodUtils import javax.swing.JComponent /** * Batch QuickFix Supported * @author caikang * @date 2016/12/08 */ class AliMissingOverrideAnnotationInspection : MissingOverrideAnnotationInspection, AliBaseInspection { private val messageKey = "com.alibaba.p3c.idea.inspection.standalone.AliMissingOverrideAnnotationInspection" constructor() /** * For Javassist */ constructor(any: Any?) : this() init { ignoreAnonymousClassMethods = false ignoreObjectMethods = false } override fun getDisplayName(): String = P3cBundle.getMessage("$messageKey.message") override fun getStaticDescription(): String? = P3cBundle.getMessage("$messageKey.desc") override fun ruleName(): String = "MissingOverrideAnnotationRule" override fun buildErrorString(vararg infos: Any): String = P3cBundle.getMessage("$messageKey.errMsg") override fun createOptionsPanel(): JComponent? = null override fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix = super.buildFix(*infos) ?: return null return DecorateInspectionGadgetsFix(fix, P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.standalone.AliMissingOverrideAnnotationInspection")) } override fun manualBuildFix(psiElement: PsiElement, isOnTheFly: Boolean): LocalQuickFix? = buildFix(psiElement) override fun getDefaultLevel(): HighlightDisplayLevel = HighlightDisplayLevels.BLOCKER override fun buildVisitor(): BaseInspectionVisitor = MissingOverrideAnnotationVisitor() private inner class MissingOverrideAnnotationVisitor : BaseInspectionVisitor() { override fun visitMethod(method: PsiMethod) { if (method.nameIdentifier == null) { return } if (method.isConstructor) { return } if (method.hasModifierProperty(PsiModifier.PRIVATE) || method.hasModifierProperty(PsiModifier.STATIC)) { return } val methodClass = method.containingClass ?: return if (ignoreAnonymousClassMethods && methodClass is PsiAnonymousClass) { return } if (hasOverrideAnnotation(method)) { return } if (!isJdk6Override(method, methodClass) && !isJdk5Override(method, methodClass)) { return } if (ignoreObjectMethods && (MethodUtils.isHashCode(method) || MethodUtils.isEquals(method) || MethodUtils.isToString(method))) { return } registerMethodError(method) } private fun hasOverrideAnnotation(element: PsiModifierListOwner): Boolean { val modifierList = element.modifierList return modifierList?.findAnnotation(CommonClassNames.JAVA_LANG_OVERRIDE) != null } private fun isJdk6Override(method: PsiMethod, methodClass: PsiClass): Boolean { val superMethods = method.findSuperMethods() var hasSupers = false for (superMethod in superMethods) { val superClass = superMethod.containingClass if (!InheritanceUtil.isInheritorOrSelf(methodClass, superClass, true)) { continue } hasSupers = true if (!superMethod.hasModifierProperty(PsiModifier.PROTECTED)) { return true } } // is override except if this is an interface method // overriding a protected method in java.lang.Object // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6501053 return hasSupers && !methodClass.isInterface } private fun isJdk5Override(method: PsiMethod, methodClass: PsiClass): Boolean { val superMethods = method.findSuperMethods() for (superMethod in superMethods) { val superClass = superMethod.containingClass if (superClass == null || !InheritanceUtil.isInheritorOrSelf(methodClass, superClass, true)) { continue } if (superClass.isInterface) { continue } if (methodClass.isInterface && superMethod.hasModifierProperty(PsiModifier.PROTECTED)) { // only true for J2SE java.lang.Object.clone(), but might // be different on other/newer java platforms continue } return true } return false } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone/MapOrSetKeyShouldOverrideHashCodeEqualsInspection.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.inspection.standalone import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.p3c.idea.util.HighlightDisplayLevels import com.alibaba.p3c.idea.util.NumberConstants import com.alibaba.p3c.idea.util.ObjectConstants import com.google.common.collect.Sets import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.ide.highlighter.JavaFileType import com.intellij.psi.CommonClassNames import com.intellij.psi.PsiClass import com.intellij.psi.PsiClassType import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiType import com.intellij.psi.PsiTypeParameter import com.intellij.psi.PsiVariable import com.siyeh.ig.BaseInspection import com.siyeh.ig.BaseInspectionVisitor import org.jetbrains.annotations.NonNls /** * @author caikang * @date 2017/03/01 */ class MapOrSetKeyShouldOverrideHashCodeEqualsInspection : BaseInspection, AliBaseInspection { val messageKey = "com.alibaba.p3c.idea.inspection.standalone.MapOrSetKeyShouldOverrideHashCodeEqualsInspection" constructor() /** * For Javassist */ constructor(any: Any?) : this() override fun getDisplayName(): String { return P3cBundle.getMessage("$messageKey.message") } override fun getStaticDescription(): String? { return P3cBundle.getMessage("$messageKey.desc") } override fun buildErrorString(vararg infos: Any): String { val type = infos[0] as PsiClassType return String.format(P3cBundle.getMessage("$messageKey.errMsg"), type.className) } override fun buildVisitor(): BaseInspectionVisitor { return MapOrSetKeyVisitor() } override fun ruleName(): String { return "MapOrSetKeyShouldOverrideHashCodeEqualsRule" } override fun getDefaultLevel(): HighlightDisplayLevel { return HighlightDisplayLevels.CRITICAL } internal enum class ClassType { /** * parameter type is Set */ SET, MAP, OTHER; override fun toString(): String { val string = super.toString() return string[0] + string.substring(1).toLowerCase() } } private class MapOrSetKeyVisitor : BaseInspectionVisitor() { private fun getClassType(aClass: PsiClass?): ClassType { return isMapOrSet(aClass, Sets.newHashSet()) } private fun isMapOrSet(aClass: PsiClass?, visitedClasses: MutableSet): ClassType { if (aClass == null) { return ClassType.OTHER } if (!visitedClasses.add(aClass)) { return ClassType.OTHER } @NonNls val className = aClass.qualifiedName if (CommonClassNames.JAVA_UTIL_SET == className) { return ClassType.SET } if (CommonClassNames.JAVA_UTIL_MAP == className) { return ClassType.MAP } val supers = aClass.supers return supers .map { isMapOrSet(it, visitedClasses) } .firstOrNull { it != ClassType.OTHER } ?: ClassType.OTHER } override fun visitVariable(variable: PsiVariable) { super.visitVariable(variable) val typeElement = variable.typeElement ?: return val type = typeElement.type as? PsiClassType ?: return val referenceElement = typeElement.innermostComponentReferenceElement ?: return val aClass = type.resolve() val collectionType = getClassType(aClass) if (collectionType == ClassType.OTHER) { return } val parameterList = referenceElement.parameterList if (parameterList == null || parameterList.typeParameterElements.size == NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { return } val psiType = parameterList.typeArguments[0] if (!redefineHashCodeEquals(psiType)) { registerError(parameterList.typeParameterElements[0], psiType) } } override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { val methodExpression = expression.methodExpression val qualifierExpression = methodExpression.qualifierExpression ?: return val type = qualifierExpression.type as? PsiClassType ?: return val aClass = type.resolve() val collectionType = getClassType(aClass) if (collectionType == ClassType.OTHER) { return } @NonNls val methodName = methodExpression.referenceName if (collectionType == ClassType.SET && ObjectConstants.METHOD_NAME_ADD != methodName) { return } if (collectionType == ClassType.MAP && ObjectConstants.METHOD_NAME_PUT != methodName) { return } val argumentList = expression.argumentList val arguments = argumentList.expressions if (collectionType == ClassType.SET && arguments.size != NumberConstants.INTEGER_SIZE_OR_LENGTH_1) { return } if (collectionType == ClassType.MAP && arguments.size != NumberConstants.INTEGER_SIZE_OR_LENGTH_2) { return } val argument = arguments[0] val argumentType = argument.type if (argumentType == null || redefineHashCodeEquals(argumentType)) { return } registerMethodCallError(expression, argumentType) } } companion object { private val skipJdkPackageJava = "java." private val skipJdkPackageJavax = "javax." private fun redefineHashCodeEquals(psiType: PsiType): Boolean { if (psiType !is PsiClassType) { return true } val psiClass = psiType.resolve() ?: return false val skip = psiClass.containingFile == null || psiClass is PsiTypeParameter || psiClass.isEnum || psiClass.isInterface || psiClass.containingFile.fileType !is JavaFileType || psiClass.qualifiedName?.startsWith(skipJdkPackageJava) ?: false || psiClass.qualifiedName?.startsWith(skipJdkPackageJavax) ?: false if (skip) { return true } val hashCodeMethods = psiClass.findMethodsByName(ObjectConstants.METHOD_NAME_HASHCODE, false) if (hashCodeMethods.size == NumberConstants.INTEGER_SIZE_OR_LENGTH_0) { return false } val equalsMethods = psiClass.findMethodsByName(ObjectConstants.METHOD_NAME_EQUALS, false) return equalsMethods.size > NumberConstants.INTEGER_SIZE_OR_LENGTH_0 } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/AliPmdProcessor.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.pmd import com.alibaba.p3c.idea.component.AliProjectComponent import com.google.common.base.Throwables import com.intellij.openapi.application.ex.ApplicationUtil import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.psi.PsiFile import net.sourceforge.pmd.PMDConfiguration import net.sourceforge.pmd.PMDException import net.sourceforge.pmd.Report import net.sourceforge.pmd.Rule import net.sourceforge.pmd.RuleContext import net.sourceforge.pmd.RuleSetFactory import net.sourceforge.pmd.RuleSets import net.sourceforge.pmd.RuleViolation import net.sourceforge.pmd.RulesetsFactoryUtils import net.sourceforge.pmd.util.ResourceLoader import java.io.IOException import java.io.StringReader import net.sourceforge.pmd.SourceCodeProcessor as PmdSourceCodeProcessor /** * @author caikang * @date 2016/12/11 */ class AliPmdProcessor private constructor(val rule: Rule? = null, val ruleSets: RuleSets? = null) { constructor(rule: Rule) : this(rule, null) constructor(ruleSets: RuleSets) : this(null, ruleSets) private val ruleSetFactory: RuleSetFactory private val configuration = PMDConfiguration() init { ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, ResourceLoader()) } fun processFile(psiFile: PsiFile, isOnTheFly: Boolean): List { configuration.setSourceEncoding(psiFile.virtualFile.charset.name()) configuration.inputPaths = psiFile.virtualFile.canonicalPath val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile) ?: return emptyList() val project = psiFile.project val aliProjectComponent = project.getComponent(AliProjectComponent::class.java) val fileContext = aliProjectComponent.getFileContext(psiFile.virtualFile) ?: return emptyList() val ctx = RuleContext() val niceFileName = psiFile.virtualFile.canonicalPath!! val report = Report.createReport(ctx, niceFileName) val processRuleSets = ruleSets ?: RuleSets().also { rs -> val ruleSet = ruleSetFactory.createSingleRuleRuleSet(rule) rs.addRuleSet(ruleSet) } LOG.debug("Processing " + ctx.sourceCodeFilename) try { val reader = StringReader(document.text) ctx.languageVersion = null if (isOnTheFly) { SourceCodeProcessor(configuration, document, fileContext, isOnTheFly).processSourceCode( reader, processRuleSets, ctx ) } else { PmdSourceCodeProcessor(configuration).processSourceCode(reader, processRuleSets, ctx) } } catch (pmde: PMDException) { LOG.debug("Error while processing file: $niceFileName", pmde.cause) report.addError(Report.ProcessingError(pmde, niceFileName)) } catch (ioe: IOException) { LOG.error("Unable to read source file: $niceFileName", ioe) } catch (pce: ProcessCanceledException) { throw pce } catch (re: RuntimeException) { val root = Throwables.getRootCause(re) if (root !is ApplicationUtil.CannotRunReadActionException) { LOG.error("RuntimeException while processing file: $niceFileName", re) } } return ctx.report.toList() } companion object { private val LOG = Logger.getInstance(AliPmdProcessor::class.java) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/SourceCodeProcessor.kt ================================================ /** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package com.alibaba.p3c.idea.pmd import com.alibaba.p3c.idea.component.AliProjectComponent.FileContext import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.util.withLockNotInline import com.alibaba.p3c.idea.util.withTryLock import com.google.common.cache.Cache import com.google.common.cache.CacheBuilder import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import net.sourceforge.pmd.PMD import net.sourceforge.pmd.PMDConfiguration import net.sourceforge.pmd.PMDException import net.sourceforge.pmd.RuleContext import net.sourceforge.pmd.RuleSets import net.sourceforge.pmd.benchmark.TimeTracker import net.sourceforge.pmd.benchmark.TimedOperationCategory import net.sourceforge.pmd.lang.Language import net.sourceforge.pmd.lang.LanguageVersion import net.sourceforge.pmd.lang.LanguageVersionHandler import net.sourceforge.pmd.lang.Parser import net.sourceforge.pmd.lang.ast.Node import net.sourceforge.pmd.lang.ast.ParseException import java.io.Reader import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit.MILLISECONDS class SourceCodeProcessor( private val configuration: PMDConfiguration, private val document: Document, private val fileContext: FileContext, private val isOnTheFly: Boolean ) { /** * Processes the input stream against a rule set using the given input * encoding. If the LanguageVersion is `null` on the RuleContext, * it will be automatically determined. Any code which wishes to process * files for different Languages, will need to be sure to either properly * set the Language on the RuleContext, or set it to `null` * first. * * @see RuleContext.setLanguageVersion * @see PMDConfiguration.getLanguageVersionOfFile * @param sourceCode * The Reader to analyze. * @param ruleSets * The collection of rules to process against the file. * @param ctx * The context in which PMD is operating. * @throws PMDException * if the input encoding is unsupported, the input stream could * not be parsed, or other error is encountered. */ @Throws(PMDException::class) fun processSourceCode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) { determineLanguage(ctx) try { ruleSets.start(ctx) processSource(sourceCode, ruleSets, ctx) } catch (pe: ParseException) { configuration.analysisCache.analysisFailed(ctx.sourceCodeFile) throw PMDException("Error while parsing " + ctx.sourceCodeFilename, pe) } catch (e: Exception) { configuration.analysisCache.analysisFailed(ctx.sourceCodeFile) throw PMDException("Error while processing " + ctx.sourceCodeFilename, e) } finally { ruleSets.end(ctx) } } private fun parse(ctx: RuleContext, sourceCode: Reader, parser: Parser): Node { TimeTracker.startOperation(TimedOperationCategory.PARSER).use { val rootNode = parser.parse(ctx.sourceCodeFilename, sourceCode) ctx.report.suppress(parser.suppressMap) return rootNode } } private fun getRootNode(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext): Node? { if (!smartFoxConfig.astCacheEnable) { return parseNode(ctx, ruleSets, sourceCode) } val node = getNode(ctx.sourceCodeFilename, isOnTheFly) if (node != null) { return node } if (document.lineCount > 3000 && isOnTheFly) { return null } val lock = fileContext.lock val readLock = lock.readLock() val writeLock = lock.writeLock() val ruleName = ruleSets.allRules.joinToString(",") { it.name } val fileName = ctx.sourceCodeFilename val readAction = { getNode(ctx.sourceCodeFilename, isOnTheFly) } val cacheNode = if (isOnTheFly) { readLock.withTryLock(50, MILLISECONDS, readAction) } else { val start = System.currentTimeMillis() LOG.info("rule:$ruleName,file:$fileName require read lock") readLock.withLockNotInline(readAction).also { LOG.info("rule:$ruleName,file:$fileName get result $it with read lock ,elapsed ${System.currentTimeMillis() - start}") } } if (cacheNode != null) { return cacheNode } val writeAction = { val finalNode = getNode(ctx.sourceCodeFilename, isOnTheFly) if (finalNode == null) { val start = System.currentTimeMillis() if (!isOnTheFly) { LOG.info("rule:$ruleName,file:$fileName parse with write lock") } parseNode(ctx, ruleSets, sourceCode).also { if (!isOnTheFly) { LOG.info("rule:$ruleName,file:$fileName get result $it parse with write lock ,elapsed ${System.currentTimeMillis() - start}") } } } else { finalNode } } return if (isOnTheFly) { writeLock.withTryLock(50, MILLISECONDS, writeAction)!! } else { writeLock.withLockNotInline( writeAction )!! } } private fun parseNode(ctx: RuleContext, ruleSets: RuleSets, sourceCode: Reader): Node { val languageVersion = ctx.languageVersion val languageVersionHandler = languageVersion.languageVersionHandler val parser = PMD.parserFor(languageVersion, configuration) val rootNode = parse(ctx, sourceCode, parser) symbolFacade(rootNode, languageVersionHandler) val language = languageVersion.language usesDFA(languageVersion, rootNode, ruleSets, language) usesTypeResolution(languageVersion, rootNode, ruleSets, language) onlyTheFlyCache.put(ctx.sourceCodeFilename, rootNode) userTriggerNodeCache.put(ctx.sourceCodeFilename, rootNode) return rootNode } private fun symbolFacade(rootNode: Node, languageVersionHandler: LanguageVersionHandler) { TimeTracker.startOperation(TimedOperationCategory.SYMBOL_TABLE) .use { languageVersionHandler.getSymbolFacade(configuration.classLoader).start(rootNode) } } private fun resolveQualifiedNames(rootNode: Node, handler: LanguageVersionHandler) { TimeTracker.startOperation(TimedOperationCategory.QUALIFIED_NAME_RESOLUTION) .use { handler.getQualifiedNameResolutionFacade(configuration.classLoader).start(rootNode) } } // private ParserOptions getParserOptions(final LanguageVersionHandler // languageVersionHandler) { // // TODO Handle Rules having different parser options. // ParserOptions parserOptions = // languageVersionHandler.getDefaultParserOptions(); // parserOptions.setSuppressMarker(configuration.getSuppressMarker()); // return parserOptions; // } private fun usesDFA(languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets, language: Language) { if (ruleSets.usesDFA(language)) { TimeTracker.startOperation(TimedOperationCategory.DFA).use { to -> val dataFlowFacade = languageVersion.languageVersionHandler.dataFlowFacade dataFlowFacade.start(rootNode) } } } private fun usesTypeResolution( languageVersion: LanguageVersion, rootNode: Node, ruleSets: RuleSets, language: Language ) { if (ruleSets.usesTypeResolution(language)) { TimeTracker.startOperation(TimedOperationCategory.TYPE_RESOLUTION).use { to -> languageVersion.languageVersionHandler.getTypeResolutionFacade(configuration.classLoader) .start(rootNode) } } } private fun usesMultifile( rootNode: Node, languageVersionHandler: LanguageVersionHandler, ruleSets: RuleSets, language: Language ) { if (ruleSets.usesMultifile(language)) { TimeTracker.startOperation(TimedOperationCategory.MULTIFILE_ANALYSIS) .use { languageVersionHandler.multifileFacade.start(rootNode) } } } private fun processSource(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) { val languageVersion = ctx.languageVersion val languageVersionHandler = languageVersion.languageVersionHandler val rootNode = getRootNode(sourceCode, ruleSets, ctx) ?: return resolveQualifiedNames(rootNode, languageVersionHandler) symbolFacade(rootNode, languageVersionHandler) val language = languageVersion.language usesDFA(languageVersion, rootNode, ruleSets, language) usesTypeResolution(languageVersion, rootNode, ruleSets, language) usesMultifile(rootNode, languageVersionHandler, ruleSets, language) val acus = listOf(rootNode) ruleSets.apply(acus, ctx, language) } private fun determineLanguage(ctx: RuleContext) { // If LanguageVersion of the source file is not known, make a // determination if (ctx.languageVersion == null) { val languageVersion = configuration.getLanguageVersionOfFile(ctx.sourceCodeFilename) ctx.languageVersion = languageVersion } } companion object { val smartFoxConfig = ServiceManager.getService(P3cConfig::class.java)!! private lateinit var onlyTheFlyCache: Cache private lateinit var userTriggerNodeCache: Cache private val LOG = Logger.getInstance(SourceCodeProcessor::class.java) init { reInitNodeCache(smartFoxConfig.astCacheTime) } fun reInitNodeCache(expireTime: Long) { onlyTheFlyCache = CacheBuilder.newBuilder().concurrencyLevel(16) .expireAfterWrite(expireTime, TimeUnit.MILLISECONDS) .maximumSize(300) .build()!! userTriggerNodeCache = CacheBuilder.newBuilder().concurrencyLevel(16) .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(300) .build()!! } fun invalidateCache(file: String) { onlyTheFlyCache.invalidate(file) userTriggerNodeCache.invalidate(file) } fun invalidUserTrigger(file: String) { userTriggerNodeCache.invalidate(file) } fun invalidateAll() { onlyTheFlyCache.invalidateAll() userTriggerNodeCache.invalidateAll() } fun getNode(file: String, isOnTheFly: Boolean): Node? { return if (isOnTheFly) onlyTheFlyCache.getIfPresent(file) else userTriggerNodeCache.getIfPresent(file) } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionDataSource.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.pmd.index import com.intellij.util.indexing.FileContent import net.sourceforge.pmd.util.datasource.DataSource import java.io.ByteArrayInputStream import java.io.IOException import java.io.InputStream /** * @author caikang * @date 2016/12/11 */ class InspectionDataSource(private val fileContent: FileContent) : DataSource { @Throws(IOException::class) override fun getInputStream(): InputStream { return ByteArrayInputStream(fileContent.content) } override fun getNiceFileName(shortNames: Boolean, inputFileName: String?): String { return fileContent.fileName } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/pmd/index/InspectionRenderer.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.pmd.index import net.sourceforge.pmd.Report import net.sourceforge.pmd.RuleViolation import net.sourceforge.pmd.renderers.AbstractRenderer import net.sourceforge.pmd.renderers.Renderer import net.sourceforge.pmd.util.datasource.DataSource import java.io.IOException /** * @author caikang * @date 2016/12/11 */ class InspectionRenderer : AbstractRenderer("InspectionRenderer", "Idea Inspection for pmd result"), Renderer { private lateinit var ruleProblems: List override fun defaultFileExtension(): String? { return null } @Throws(IOException::class) override fun start() { } override fun startFileAnalysis(dataSource: DataSource) { } @Throws(IOException::class) override fun renderFileReport(report: Report) { ruleProblems = report.map { it } } @Throws(IOException::class) override fun end() { } fun getViolations(): List { return ruleProblems } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AliQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.intellij.codeInspection.LocalQuickFix import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiIdentifier import com.intellij.psi.PsiLocalVariable import com.intellij.psi.PsiMember import com.intellij.psi.PsiParameter /** * * * @author caikang * @date 2017/02/27 */ interface AliQuickFix : LocalQuickFix { val ruleName: String val onlyOnThFly: Boolean override fun getFamilyName(): String { return groupName } companion object { const val groupName = "Ali QuickFix" fun doQuickFix(newIdentifier: String, project: Project, psiIdentifier: PsiIdentifier) { val offset = psiIdentifier.textOffset val cannotFix = psiIdentifier.parent !is PsiMember && !(psiIdentifier.parent is PsiLocalVariable || psiIdentifier.parent is PsiParameter) if (cannotFix) { return } val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return editor.caretModel.moveToOffset(psiIdentifier.textOffset) val anAction = ActionManager.getInstance().getAction("RenameElement") val psiFile = psiIdentifier.containingFile commitDocumentIfNeeded(psiFile, project) val event = AnActionEvent.createFromDataContext("MainMenu", anAction.templatePresentation) { when (it) { CommonDataKeys.PROJECT.name -> project CommonDataKeys.EDITOR.name -> editor CommonDataKeys.PSI_FILE.name -> psiFile CommonDataKeys.PSI_ELEMENT.name -> psiIdentifier.parent else -> null } } val psiFacade = JavaPsiFacade.getInstance(project) val factory = psiFacade.elementFactory anAction.actionPerformed(event) // origin PsiIdentifier is unavailable psiFile.findElementAt(offset)?.replace(factory.createIdentifier(newIdentifier)) } private fun commitDocumentIfNeeded(file: PsiFile?, project: Project) { if (file == null) { return } val manager = PsiDocumentManager.getInstance(project) val cachedDocument = manager.getCachedDocument(file) ?: return manager.commitDocument(cachedDocument) } } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/AvoidStartWithDollarAndUnderLineNamingQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.alibaba.p3c.idea.i18n.P3cBundle import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.intellij.psi.PsiIdentifier import org.apache.commons.lang3.StringUtils /** * * * @author caikang * @date 2017/02/28 */ object AvoidStartWithDollarAndUnderLineNamingQuickFix : AliQuickFix { override val ruleName: String get() = "AvoidStartWithDollarAndUnderLineNamingRule" override val onlyOnThFly: Boolean get() = true override fun getName(): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.delete_$") } override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return val identifier = psiIdentifier.text val resultName = StringUtils.replacePattern(identifier, "^[\$_]+", "") if (resultName.toLongOrNull() != null) { return } AliQuickFix.doQuickFix(resultName, project, psiIdentifier) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ClassMustHaveAuthorQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.alibaba.p3c.idea.i18n.P3cBundle import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass import com.intellij.psi.javadoc.PsiDocToken import com.siyeh.ig.InspectionGadgetsFix /** * * * @author caikang * @date 2017/02/27 */ object ClassMustHaveAuthorQuickFix : InspectionGadgetsFix(), AliQuickFix { val tag = "@author ${System.getProperty("user.name") ?: System.getenv("USER")}" override fun doFix(project: Project?, descriptor: ProblemDescriptor?) { descriptor ?: return val psiClass = descriptor.psiElement as? PsiClass ?: descriptor.psiElement?.parent as? PsiClass ?: return val document = psiClass.docComment val psiFacade = JavaPsiFacade.getInstance(project) val factory = psiFacade.elementFactory if (document == null) { val doc = factory.createDocCommentFromText(""" /** * $tag */ """) if (psiClass.isEnum) { psiClass.containingFile.addAfter(doc, psiClass.prevSibling) } else { psiClass.addBefore(doc, psiClass.firstChild) } return } val regex = Regex("Created by (.*) on (.*)\\.") for (line in document.descriptionElements) { if (line is PsiDocToken && line.text.contains(regex)) { val groups = regex.find(line.text)?.groups ?: continue val author = groups[1]?.value ?: continue val date = groups[2]?.value ?: continue document.addBefore(factory.createDocTagFromText("@date $date"), line) document.addBefore(factory.createDocTagFromText("@author $author"), line) line.delete() return } } if (document.tags.isNotEmpty()) { document.addBefore(factory.createDocTagFromText(tag), document.tags[0]) return } document.add(factory.createDocTagFromText(tag)) } override val ruleName: String get() = "ClassMustHaveAuthorRule" override val onlyOnThFly: Boolean get() = true override fun getName(): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.generate.author") } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/ConstantFieldShouldBeUpperCaseQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.alibaba.p3c.idea.i18n.P3cBundle import com.google.common.base.Splitter import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.intellij.psi.PsiIdentifier /** * * * @author caikang * @date 2017/02/28 */ object ConstantFieldShouldBeUpperCaseQuickFix : AliQuickFix { val separator = '_' override fun getName(): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.field.to.upperCaseWithUnderscore") } override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return val identifier = psiIdentifier.text val list = Splitter.on(separator).trimResults().omitEmptyStrings().splitToList(identifier) val resultName = list.joinToString(separator.toString()) { separateCamelCase(it).toUpperCase() } AliQuickFix.doQuickFix(resultName, project, psiIdentifier) } private fun separateCamelCase(name: String): String { val translation = StringBuilder() for (i in 0 until name.length - 1) { val character = name[i] val next = name[i + 1] if (Character.isUpperCase(character) && !next.isUpperCase() && translation.isNotEmpty()) { translation.append(separator) } if (character != separator) { translation.append(character) } } val last = name.last() if (last != separator) { translation.append(last) } return translation.toString() } override val ruleName: String get() = "ConstantFieldShouldBeUpperCaseRule" override val onlyOnThFly: Boolean get() = true } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/DecorateInspectionFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.siyeh.ig.InspectionGadgetsFix /** * * * @author caikang * @date 2017/03/02 */ class DecorateInspectionGadgetsFix(val fix: InspectionGadgetsFix, internal val name: String, internal val familyName: String = name) : InspectionGadgetsFix() { override fun getName(): String { return name } override fun doFix(project: Project, descriptor: ProblemDescriptor) { fix.applyFix(project, descriptor) } override fun getFamilyName(): String { return familyName } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/LowerCamelCaseVariableNamingQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.alibaba.p3c.idea.i18n.P3cBundle import com.google.common.base.Splitter import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.project.Project import com.intellij.psi.PsiIdentifier /** * * * @author caikang * @date 2017/02/28 */ object LowerCamelCaseVariableNamingQuickFix : AliQuickFix { override val ruleName: String get() = "LowerCamelCaseVariableNamingRule" override val onlyOnThFly: Boolean get() = true override fun getName(): String { return P3cBundle.getMessage("com.alibaba.p3c.idea.quickfix.variable.lowerCamelCase") } override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val psiIdentifier = descriptor.psiElement as? PsiIdentifier ?: return val identifier = psiIdentifier.text val resultName = toLowerCamelCase(identifier) AliQuickFix.doQuickFix(resultName, project, psiIdentifier) } private fun toLowerCamelCase(identifier: String): String { val list = Splitter.onPattern("[^a-z0-9A-Z]+").trimResults() .omitEmptyStrings().split(identifier).toList() val result = list.mapIndexed { i, s -> val charArray = s.toCharArray() charArray[0] = if (i == 0) charArray[0].toLowerCase() else charArray[0].toUpperCase() String(charArray) } return result.joinToString("") } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/quickfix/VmQuietReferenceQuickFix.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.quickfix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/01/26 */ object VmQuietReferenceQuickFix : AliQuickFix { override val onlyOnThFly: Boolean get() = true override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val textRange = descriptor.textRangeInElement ?: return val document = FileDocumentManager.getInstance().getDocument( descriptor.startElement.containingFile.virtualFile) ?: return document.insertString(textRange.startOffset + 1, "!") } override val ruleName = "UseQuietReferenceNotationRule" override fun getName(): String { return "为变量添加!" } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/DocumentUtils.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.intellij.openapi.editor.Document import com.intellij.openapi.util.TextRange /** * * * @author caikang * @date 2017/03/16 6 */ object DocumentUtils { private val PMD_TAB_SIZE = 8 fun calculateRealOffset(document: Document, line: Int, pmdColumn: Int): Int { val maxLine = document.lineCount if (maxLine < line) { return -1 } val lineOffset = document.getLineStartOffset(line - 1) return lineOffset + calculateRealColumn(document, line, pmdColumn) } fun calculateLineStart(document: Document, line: Int): Int { val maxLine = document.lineCount if (maxLine < line) { return -1 } return document.getLineStartOffset(line - 1) } fun calculateRealColumn(document: Document, line: Int, pmdColumn: Int): Int { var realColumn = pmdColumn - 1 val minusSize = PMD_TAB_SIZE - 1 val docLine = line - 1 val lineStartOffset = document.getLineStartOffset(docLine) val lineEndOffset = document.getLineEndOffset(docLine) val text = document.getText(TextRange(lineStartOffset, lineEndOffset)) text.forEachIndexed { i, c -> if (c == '\t') { realColumn -= minusSize } if (i >= realColumn) { return@forEachIndexed } } return realColumn } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightDisplayLevels.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.intellij.codeHighlighting.HighlightDisplayLevel /** * * * @author caikang * @date 2017/02/04 */ object HighlightDisplayLevels { val BLOCKER = HighlightDisplayLevel(HighlightSeverities.BLOCKER, HighlightDisplayLevel.ERROR.icon) val CRITICAL = HighlightDisplayLevel(HighlightSeverities.CRITICAL, HighlightDisplayLevel.WARNING.icon) val MAJOR = HighlightDisplayLevel(HighlightSeverities.MAJOR, HighlightDisplayLevel.WEAK_WARNING.icon) } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightInfoTypes.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.intellij.codeInsight.daemon.impl.HighlightInfoType import com.intellij.openapi.editor.colors.CodeInsightColors /** * * * @author caikang * @date 2017/02/04 */ object HighlightInfoTypes { val BLOCKER: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.BLOCKER, CodeInsightColors.ERRORS_ATTRIBUTES) val CRITICAL: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.CRITICAL, CodeInsightColors.WARNINGS_ATTRIBUTES) val MAJOR: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.MAJOR, CodeInsightColors.WEAK_WARNING_ATTRIBUTES) } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/HighlightSeverities.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.intellij.lang.annotation.HighlightSeverity /** * * * @author caikang * @date 2017/02/04 */ object HighlightSeverities { val MAJOR = HighlightSeverity("MAJOR", 397) /** * The standard severity level for warning annotations. */ val CRITICAL = HighlightSeverity("CRITICAL", 398) /** * The standard severity level for error annotations. */ val BLOCKER = HighlightSeverity("BLOCKER", 399) } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/NumberConstants.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util /** * @author caikang * @date 2016/12/28 */ object NumberConstants { val INTEGER_SIZE_OR_LENGTH_0 = 0 val INTEGER_SIZE_OR_LENGTH_1 = 1 val INTEGER_SIZE_OR_LENGTH_2 = 2 val INDEX_0 = 0 val INDEX_1 = 1 } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/ObjectConstants.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util /** * @author caikang * @date 2016/12/28 */ object ObjectConstants { val METHOD_NAME_EQUALS = "equals" val METHOD_NAME_HASHCODE = "hashCode" val METHOD_NAME_ADD = "add" val METHOD_NAME_PUT = "put" val CLASS_LITERAL = "class" val INTERFACE_LITERAL = "interface" val ENUM_LITERAL = "enum" } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/ProblemsUtils.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.alibaba.p3c.pmd.lang.java.rule.comment.AvoidCommentBehindStatementRule import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemHighlightType import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiField import com.intellij.psi.PsiFile import com.intellij.psi.PsiIdentifier import com.intellij.psi.PsiJavaToken import com.intellij.psi.PsiKeyword import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.impl.source.tree.ElementType /** * * * @author caikang * @date 2017/03/16 6 */ object ProblemsUtils { private val highlightLineRules = setOf(AvoidCommentBehindStatementRule::class.java.simpleName) fun createProblemDescriptorForPmdRule(psiFile: PsiFile, manager: InspectionManager, isOnTheFly: Boolean, ruleName: String, desc: String, start: Int, end: Int, checkLine: Int = 0, quickFix: (PsiElement) -> LocalQuickFix? = { QuickFixes.getQuickFix(ruleName, isOnTheFly) }): ProblemDescriptor? { val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile) ?: return null if (highlightLineRules.contains(ruleName) && checkLine <= document.lineCount) { val lineNumber = if (start >= document.textLength) { document.lineCount - 1 } else { document.getLineNumber(start) } val textRange = TextRange(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber)) return createTextRangeProblem(manager, textRange, isOnTheFly, psiFile, ruleName, desc) } if (psiFile.virtualFile.canonicalPath!!.endsWith(".vm")) { return createTextRangeProblem(manager, TextRange(start, end), isOnTheFly, psiFile, ruleName, desc) } var psiElement = psiFile.findElementAt(start) ?: return null psiElement = transform(psiElement) ?: return null var endElement = if (start == end) psiElement else getEndElement(psiFile, psiElement, end) if (psiElement != endElement && endElement.parent is PsiField) { psiElement = endElement } if (endElement is PsiWhiteSpace) { endElement = psiElement } if (psiElement is PsiWhiteSpace) { val textRange = TextRange(start, end) return createTextRangeProblem(manager, textRange, isOnTheFly, psiFile, ruleName, desc) } if (psiElement.textRange.startOffset >= endElement.textRange.endOffset) { if (!(psiElement is PsiFile && endElement is PsiFile)) { return null } endElement = psiElement } return manager.createProblemDescriptor(psiElement, endElement, desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, quickFix(psiElement)) } private fun getEndElement(psiFile: PsiFile, psiElement: PsiElement, endOffset: Int): PsiElement { var endElement = psiFile.findElementAt(endOffset) if (endElement is PsiJavaToken && endElement.tokenType === ElementType.SEMICOLON) { endElement = psiFile.findElementAt(endOffset - 1) } if (endElement is PsiIdentifier) { return endElement } if (psiElement is PsiIdentifier) { return psiElement } if (endElement == null || endElement is PsiWhiteSpace || psiElement.textRange.startOffset >= endElement.textRange.endOffset) { endElement = psiElement } return endElement } private fun transform(element: PsiElement): PsiElement? { var psiElement: PsiElement? = element while (psiElement is PsiWhiteSpace) { psiElement = psiElement.getNextSibling() } if (psiElement == null) { return null } if (psiElement is PsiKeyword && psiElement.text != null && (ObjectConstants.CLASS_LITERAL == psiElement.text || ObjectConstants.INTERFACE_LITERAL == psiElement.text || ObjectConstants.ENUM_LITERAL == psiElement.text) && psiElement.parent is PsiClass) { val parent = psiElement.parent as PsiClass val identifier = parent.nameIdentifier return identifier ?: psiElement } return psiElement } private fun createTextRangeProblem(manager: InspectionManager, textRange: TextRange, isOnTheFly: Boolean, psiFile: PsiFile, ruleName: String, desc: String, quickFix: () -> LocalQuickFix? = { QuickFixes.getQuickFix(ruleName, isOnTheFly) }): ProblemDescriptor { return manager.createProblemDescriptor(psiFile, textRange, desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, quickFix()) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/QuickFixes.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.util import com.alibaba.p3c.idea.quickfix.AvoidStartWithDollarAndUnderLineNamingQuickFix import com.alibaba.p3c.idea.quickfix.ClassMustHaveAuthorQuickFix import com.alibaba.p3c.idea.quickfix.ConstantFieldShouldBeUpperCaseQuickFix import com.alibaba.p3c.idea.quickfix.LowerCamelCaseVariableNamingQuickFix import com.alibaba.p3c.idea.quickfix.VmQuietReferenceQuickFix import com.intellij.codeInspection.LocalQuickFix /** * * * @author caikang * @date 2017/02/06 */ object QuickFixes { val quickFixes = mutableMapOf(VmQuietReferenceQuickFix.ruleName to VmQuietReferenceQuickFix, ClassMustHaveAuthorQuickFix.ruleName to ClassMustHaveAuthorQuickFix, ConstantFieldShouldBeUpperCaseQuickFix.ruleName to ConstantFieldShouldBeUpperCaseQuickFix, AvoidStartWithDollarAndUnderLineNamingQuickFix.ruleName to AvoidStartWithDollarAndUnderLineNamingQuickFix, LowerCamelCaseVariableNamingQuickFix.ruleName to LowerCamelCaseVariableNamingQuickFix) fun getQuickFix(rule: String, isOnTheFly: Boolean): LocalQuickFix? { val quickFix = quickFixes[rule] ?: return null if (!quickFix.onlyOnThFly) { return quickFix } if (!isOnTheFly && quickFix.onlyOnThFly) { return null } return quickFix } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/util/withLockNotInline.kt ================================================ package com.alibaba.p3c.idea.util import com.intellij.openapi.progress.ProcessCanceledException import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit import java.util.concurrent.locks.Lock /** * @date 2020/06/14 * @author caikang */ fun Lock.withLockNotInline(action: () -> T?): T? { lock() try { return action() } finally { unlock() } } fun Semaphore.withAcquire(action: () -> T?): T? { acquire() try { return action() } finally { release() } } fun Lock.withTryLock(time: Long, timeUnit: TimeUnit, action: () -> T?): T? { if (!tryLock(time, timeUnit)) { throw ProcessCanceledException() } try { return action() } finally { unlock() } } fun Semaphore.withTryAcquire(time: Long, timeUnit: TimeUnit, action: () -> T?): T? { if (!tryAcquire(time, timeUnit)) { throw ProcessCanceledException() } try { return action() } finally { release() } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandler.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.vcs import com.alibaba.p3c.idea.action.AliInspectionAction import com.alibaba.p3c.idea.compatible.inspection.Inspections import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.alibaba.smartfox.idea.common.util.BalloonNotifications import com.intellij.analysis.AnalysisScope import com.intellij.codeInspection.InspectionManager import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.ex.InspectionManagerEx import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Computable import com.intellij.openapi.util.Ref import com.intellij.openapi.vcs.CheckinProjectPanel import com.intellij.openapi.vcs.VcsBundle import com.intellij.openapi.vcs.changes.CommitExecutor import com.intellij.openapi.vcs.checkin.CheckinHandler import com.intellij.openapi.vcs.checkin.CheckinHandlerUtil import com.intellij.openapi.vcs.ui.RefreshableOnComponent import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiManager import com.intellij.ui.NonFocusableCheckBox import com.intellij.util.ExceptionUtil import com.intellij.util.PairConsumer import java.awt.BorderLayout import java.util.ArrayList import java.util.Arrays import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger import javax.swing.JComponent import javax.swing.JPanel /** * * @author yaohui.wyh * @date 2017/03/21 * @author caikang * @date 2017/05/04 */ class AliCodeAnalysisCheckinHandler( private val myProject: Project, private val myCheckinPanel: CheckinProjectPanel ) : CheckinHandler() { private val dialogTitle = "Alibaba Code Analyze" private val cancelText = "&Cancel" private val commitText = "&Commit Anyway" private val waitingText = "Wait" val log = Logger.getInstance(javaClass) override fun getBeforeCheckinConfigurationPanel(): RefreshableOnComponent? { val checkBox = NonFocusableCheckBox("Alibaba Code Guidelines") return object : RefreshableOnComponent { override fun getComponent(): JComponent { val panel = JPanel(BorderLayout()) panel.add(checkBox) val dumb = DumbService.isDumb(myProject) checkBox.isEnabled = !dumb checkBox.toolTipText = if (dumb) { "Code analysis is impossible until indices are up-to-date" } else { "" } return panel } override fun refresh() {} override fun saveState() { getSettings().analysisBeforeCheckin = checkBox.isSelected } override fun restoreState() { checkBox.isSelected = getSettings().analysisBeforeCheckin } } } private fun getSettings(): P3cConfig { return ServiceManager.getService(P3cConfig::class.java) } override fun beforeCheckin( executor: CommitExecutor?, additionalDataConsumer: PairConsumer ): CheckinHandler.ReturnResult { if (!getSettings().analysisBeforeCheckin) { return CheckinHandler.ReturnResult.COMMIT } if (DumbService.getInstance(myProject).isDumb) { if (Messages.showOkCancelDialog( myProject, "Code analysis is impossible until indices are up-to-date", dialogTitle, waitingText, commitText, null ) == Messages.OK ) { return CheckinHandler.ReturnResult.CANCEL } return CheckinHandler.ReturnResult.COMMIT } val virtualFiles = CheckinHandlerUtil.filterOutGeneratedAndExcludedFiles(myCheckinPanel.virtualFiles, myProject) val hasViolation = hasViolation(virtualFiles, myProject) if (!hasViolation) { BalloonNotifications.showSuccessNotification( "No suspicious code found!", myProject, "Analyze Finished" ) return CheckinHandler.ReturnResult.COMMIT } if (Messages.showOkCancelDialog( myProject, "Found suspicious code,continue commit?", dialogTitle, commitText, cancelText, null ) == Messages.OK ) { return CheckinHandler.ReturnResult.COMMIT } else { doAnalysis(myProject, virtualFiles.toTypedArray()) return CheckinHandler.ReturnResult.CLOSE_WINDOW } } fun doAnalysis(project: Project, virtualFiles: Array) { val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx val analysisScope = AnalysisScope( project, ArrayList(Arrays.asList(*virtualFiles)) ) val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } AliInspectionAction.createContext(tools, managerEx, null, false, analysisScope) .doInspections(analysisScope) } private fun hasViolation(virtualFiles: List, project: Project): Boolean { ApplicationManager.getApplication().assertIsDispatchThread() PsiDocumentManager.getInstance(myProject).commitAllDocuments() if (ApplicationManager.getApplication().isWriteAccessAllowed) throw RuntimeException( "Must not run under write action" ) val result = AtomicBoolean(false) val exception = Ref.create() ProgressManager.getInstance().run( object : Task.Modal(myProject, VcsBundle.message("checking.code.smells.progress.title"), true) { override fun run(progress: ProgressIndicator) { try { val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } val inspectionManager = InspectionManager.getInstance(project) val psiManager = PsiManager.getInstance(project) val count = AtomicInteger(0) val hasViolation = virtualFiles.asSequence().any { file -> ApplicationManager.getApplication().runReadAction(Computable { val psiFile = psiManager.findFile(file) ?: return@Computable false val curCount = count.incrementAndGet() progress.text = file.canonicalPath progress.fraction = curCount.toDouble() / virtualFiles.size.toDouble() return@Computable tools.any { progress.checkCanceled() val tool = it.tool as LocalInspectionTool val aliTool = tool as AliBaseInspection progress.text2 = aliTool.ruleName() val problems = tool.processFile(psiFile, inspectionManager) problems.size > 0 } }) } result.set(hasViolation) } catch (e: ProcessCanceledException) { result.set(false) } catch (e: Exception) { log.error(e) exception.set(e) } } }) if (!exception.isNull) { val t = exception.get() ExceptionUtil.rethrowAllAsUnchecked(t) } return result.get() } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/vcs/AliCodeAnalysisCheckinHandlerFactory.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.p3c.idea.vcs import com.intellij.openapi.vcs.CheckinProjectPanel import com.intellij.openapi.vcs.changes.CommitContext import com.intellij.openapi.vcs.checkin.CheckinHandler import com.intellij.openapi.vcs.checkin.CheckinHandlerFactory /** * * * @author caikang * @date 2017/05/04 */ class AliCodeAnalysisCheckinHandlerFactory : CheckinHandlerFactory() { override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler { return AliCodeAnalysisCheckinHandler(panel.project, panel) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseApplicationComponent.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.idea.common.component import com.alibaba.smartfox.idea.common.util.PluginVersions import com.intellij.openapi.components.ApplicationComponent /** * * * @author caikang * @date 2017/05/11 */ interface AliBaseApplicationComponent : ApplicationComponent { override fun getComponentName(): String { return "${PluginVersions.pluginId.idString}-${javaClass.name}" } override fun disposeComponent() { } override fun initComponent() { } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/component/AliBaseProjectComponent.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.idea.common.component import com.alibaba.smartfox.idea.common.util.PluginVersions import com.intellij.openapi.components.ProjectComponent /** * * * @author caikang * @date 2017/04/28 */ interface AliBaseProjectComponent : ProjectComponent { override fun getComponentName(): String { return "${PluginVersions.pluginId.idString}-${javaClass.name}" } override fun disposeComponent() { } override fun projectClosed() { } override fun initComponent() { } override fun projectOpened() { } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/BalloonNotifications.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.idea.common.util import com.intellij.notification.NotificationDisplayType import com.intellij.notification.NotificationGroup import com.intellij.notification.NotificationListener import com.intellij.notification.NotificationType import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.ui.Messages import java.awt.Component import java.net.UnknownHostException /** * * * @author caikang * @date 2017/05/08 */ object BalloonNotifications { val displayId = "SmartFox Intellij IDEA Balloon Notification" val balloonGroup = NotificationGroup(displayId, NotificationDisplayType.BALLOON, true) val stickyBalloonDisplayId = "SmartFox Intellij IDEA Notification" val stickyBalloonGroup = NotificationGroup(stickyBalloonDisplayId, NotificationDisplayType.STICKY_BALLOON, true) val TITLE = "SmartFox Intellij IDEA Plugin" fun showInfoDialog(component: Component, title: String, message: String) { Messages.showInfoMessage(component, message, title) } fun showErrorDialog(component: Component, title: String, errorMessage: String) { Messages.showErrorDialog(component, errorMessage, title) } fun showErrorDialog(component: Component, title: String, e: Exception) { if (isOperationCanceled(e)) { return } Messages.showErrorDialog(component, getErrorTextFromException(e), title) } fun showSuccessNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, title: String = TITLE, sticky: Boolean = false) { showNotification(message, project, title, NotificationType.INFORMATION, null, sticky) } fun showWarnNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, title: String = TITLE, sticky: Boolean = false) { showNotification(message, project, title, NotificationType.WARNING, null, sticky) } fun showErrorNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, title: String = TITLE, sticky: Boolean = false) { showNotification(message, project, title, NotificationType.ERROR, null, sticky) } fun showSuccessNotification(message: String, project: Project?, notificationListener: NotificationListener, title: String = TITLE, sticky: Boolean = false) { showNotification(message, project, title, NotificationType.INFORMATION, notificationListener, sticky) } fun showNotification(message: String, project: Project? = ProjectManager.getInstance().defaultProject, title: String = TITLE, notificationType: NotificationType = NotificationType.INFORMATION, notificationListener: NotificationListener? = null, sticky: Boolean = false) { val group = if (sticky) { stickyBalloonGroup } else { balloonGroup } group.createNotification(title, message, notificationType, notificationListener).notify(project) } private fun isOperationCanceled(e: Exception): Boolean { return e is ProcessCanceledException } fun getErrorTextFromException(e: Exception): String { if (e is UnknownHostException) { return "Unknown host: " + e.message } return e.message ?: "" } } object LogNotifications { val group = NotificationGroup(BalloonNotifications.displayId, NotificationDisplayType.NONE, true) fun log(message: String, project: Project? = ProjectManager.getInstance().defaultProject, title: String = BalloonNotifications.TITLE, notificationType: NotificationType = NotificationType.INFORMATION, notificationListener: NotificationListener? = null) { group.createNotification(title, message, notificationType, notificationListener).notify(project) } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/CommonExtensions.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.idea.common.util import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/06/19 */ fun Class.getService(): T { return ServiceManager.getService(this) } fun Class.getService(project: Project): T { return ServiceManager.getService(project, this) } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/smartfox/idea/common/util/PluginVersions.kt ================================================ /* * Copyright 1999-2017 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.smartfox.idea.common.util import com.intellij.ide.plugins.IdeaPluginDescriptor import com.intellij.ide.plugins.PluginManager import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.extensions.PluginId /** * @author caikang */ object PluginVersions { val baseVersion141 = 141 val baseVersion143 = 143 val baseVersion145 = 145 val baseVersion162 = 162 val baseVersion163 = 163 val baseVersion171 = 171 val pluginId: PluginId = (javaClass.classLoader as PluginClassLoader).pluginId val pluginDescriptor: IdeaPluginDescriptor = PluginManager.getPlugin(pluginId)!! /** * 获取当前安装的 plugin版本 */ val pluginVersion: String get() = pluginDescriptor.version /** * 获取当前使用的IDE版本 */ val ideVersion: String get() { val applicationInfo = ApplicationInfo.getInstance() return applicationInfo.fullVersion + "_" + applicationInfo.build } val baseVersion: Int get() { val applicationInfo = ApplicationInfo.getInstance() return applicationInfo.build.baselineVersion } } ================================================ FILE: idea-plugin/p3c-common/src/main/resources/messages/P3cBundle.xml ================================================ 阿里编码规约 切换语言至英文(English) 切换语言至中文 重启后生效]]> 编码规约扫描 关闭实时检测功能 打开实时检测功能 删除开头的 $ 或者 _ 添加/提取 @author 修正为以下划线分隔大写模式 修改为小写驼峰命名(lowerCamelCase) 为变量添加! 替换为 通过类 '%s' 直接访问静态成员 '%s.%s' 添加 @Override 注解 为语句加上大括号 翻转 equals 调用 修改为 String[] str 模式 'l' 替换为 'L' #ref #loc]]> 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。 不应该通过类实例访问静态成员 %s #loc 不能使用过时的类或方法。 所有的覆写方法,必须加@Override注解。 #ref()缺少 '@Override' 注解 #loc]]> Map/Set的key为自定义对象时,必须重写hashCode和equals。 参数类型 %s 没有重写hashCode和equals #loc #ref 是非线程安全的,请加锁或者使用局部变量 #loc]]> #ref 没有加大括号 #loc]]> #ref 应该作为方法 "%s()"的调用方,而不是参数 #loc]]> #ref 数组定义格式错误 #loc]]> #ref 应该以大写L结尾 #loc]]> #ref #loc]]> 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。 ]]> 不能使用过时的类或方法。
说明:java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。 ]]>
所有的覆写方法,必须加@Override注解。
反例:getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。 ]]>
Map/Set的key为自定义对象时,必须重写hashCode和equals。
关于hashCode和equals的处理,遵循如下规则:
 1) 只要重写equals,就必须重写hashCode。
 2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
 3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
]]>
================================================ FILE: idea-plugin/p3c-common/src/main/resources/messages/P3cBundle_en.xml ================================================ Alibaba Coding Guidelines Switch language to English Switch language to Chinese restart to get effect.]]> Alibaba Coding Guidelines Analyze Close inspection on the fly Open inspection on the fly Delete starting '$' or '_' Add/Extract @author Refactor to upper case with underscore Refactor to lower camel case name Add '!' for variable Replace with Access static '%s.%s' via class '%s' reference. Add @Override annotation Add braces to statement Flip 'equals()' Replace with Java-style array declaration Replace 'l' with 'L' #ref should replace with equals for wrapper type #loc]]> A static field or method should be directly referred by its class name instead of its corresponding object name. %s access static member via class instance is prohibited #loc Using a deprecated class or method is prohibited. An overridden method from an interface or abstract class must be marked with @Override annotation. #ref() #loc]]> Custom class must override 'hashCode' and 'equals' while use as key for Map or value for Set. Type of parameter %s does not override 'hashCode' and 'equals' #loc #ref is unsafe,lock or use local variable #loc]]> #ref without braces #loc]]> #ref is argument of '%s()', instead of its target #loc]]> #ref ends with lowercase 'l' #loc]]> #ref #loc]]> A static field or method should be directly referred by its class name instead of its corresponding object name. ]]> Using a deprecated class or method is prohibited.
Note: For example, decode(String source, String encode) should be used instead of the deprecated method decode(String encodeStr). Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to check out what its new implementation is. ]]>
An overridden method from an interface or abstract class must be marked with @Override annotation.
Counter example: For getObject() and get0bject(), the first one has a letter 'O', and the second one has a number '0'. To accurately determine whether the overriding is successful, an @Override annotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately. ]]>
Custom class must override 'hashCode' and 'equals' while use as key for Map or value for Set.
The usage of hashCode and equals should follow:
 1) Override hashCode if equals is overridden.
 2) These two methods must be overridden for Set since they are used to ensure that no duplicate object will be inserted in Set.
 3) These two methods must be overridden if self-defined object is used as the key of Map.
Note: String can be used as the key of Map since these two methods have been rewritten. ]]>
================================================ FILE: idea-plugin/p3c-common/src/main/resources/rulesets/java/ali-pmd.xml ================================================ ================================================ FILE: idea-plugin/p3c-common/src/main/resources/tpl/StaticDescriptionTemplate.ftl ================================================ <#ftl output_format="HTML">
${message}<#if description??>
${description}
<#list examples as example>
${example}
================================================ FILE: idea-plugin/p3c-idea/build.gradle ================================================ plugins { id "org.jetbrains.intellij" version '0.4.5' } apply plugin: 'kotlin' apply plugin: 'idea' def myPlugins = ['git4idea'] def versionDotIndex = idea_version.indexOf('.') def intVersion = versionDotIndex == -1 ? Integer.parseInt(idea_version) : Integer.parseInt(idea_version.substring(0, versionDotIndex)) if (intVersion >= 2019 || (intVersion < 1000 && intVersion >= 193)) { myPlugins = ['git4idea', 'java'] } intellij { version idea_version plugins = myPlugins pluginName plugin_name updateSinceUntilBuild false sandboxDirectory "idea-sandbox" } version plugin_version dependencies { compile group: 'org.freemarker', name: 'freemarker', version: '2.3.25-incubating' compile project(':p3c-common') compile group: 'org.javassist', name: 'javassist', version: '3.21.0-GA' } ================================================ FILE: idea-plugin/p3c-idea/src/main/resources/META-INF/p3c.xml ================================================ com.alibaba.p3c.idea.component.CommonSettingsApplicationComponent com.alibaba.p3c.idea.component.AliProjectComponent ================================================ FILE: idea-plugin/p3c-idea/src/main/resources/META-INF/plugin.xml ================================================ com.alibaba.p3c.smartfox Alibaba Java Coding Guidelines Alibaba Java Coding Guidelines plugin support. 2.1.0
  • Fix idea 2020.2 Ultimate compatibility issue
  • Minimum supported idea version up to 2018.3
  • Kotlin upgrade to 1.3.72
  • Disable real time inspect if file lines more than 3000 lines
  • Fix https://github.com/alibaba/p3c/issues/722
  • Fix https://github.com/alibaba/p3c/issues/620
    • 2.0.2
    • Fix idea 2020.1 Ultimate compatibility issue
      2.0.1
    • Fix idea 2019.3 compatibility issue
    • fix https://github.com/alibaba/p3c/issues/540
    • fix https://github.com/alibaba/p3c/issues/209
    • fix https://github.com/alibaba/p3c/issues/579
    • Add code style rule of lock
      2.0.0
    • supported min idea version 2016.1(145.258.11)
    • supported min jdk version 1.8
    • fix persistent compatibility issue
    • fix https://github.com/alibaba/p3c/issues/430
    • fix https://github.com/alibaba/p3c/issues/454
    • fix https://github.com/alibaba/p3c/issues/409
    • add rule To judge the equivalence of floating-point numbers.
    • add annotation process for rule ClassMustHaveAuthorRule
      1.0.5
    • Add rule [Recommended] The total number of lines for a method should not be more than 80.
    • Add rule [Recommended] Avoid using the negation operator '!'.
    • Add rule [Mandatory] When doing date formatting, "y" should be written in lowercase for "year" in a pattern statement
    • https://github.com/alibaba/p3c/issues/264
      1.0.4
    • fix https://github.com/alibaba/p3c/issues/217
    • fix https://github.com/alibaba/p3c/issues/208
    • fix https://github.com/alibaba/p3c/issues/195
    ]]>
    alibaba 2.0.0 com.intellij.velocity com.intellij.modules.java com.intellij.modules.platform com.intellij.modules.lang com.intellij.modules.vcs com.intellij.modules.xml com.intellij.modules.xdebugger
    ================================================ FILE: idea-plugin/settings.gradle ================================================ include 'p3c-idea' include 'p3c-common' ================================================ FILE: license.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 1999-2017 Alibaba Group. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: p3c-formatter/eclipse-codestyle.xml ================================================ ================================================ FILE: p3c-formatter/eclipse-codetemplate.xml ================================================