Repository: XenoAmess/p3c Branch: xenoamess_maintain_fork Commit: 79916bfd55a6 Files: 317 Total size: 1.1 MB Directory structure: gitextract_gvecwtd9/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── ------.md │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── rule-issue-template.md │ ├── dependabot.yml │ └── workflows/ │ ├── auto-merge.yml │ └── build.yml ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── AGENTS.md ├── README.md ├── build.cmd ├── deploy.cmd ├── 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 │ │ │ │ ├── activity/ │ │ │ │ │ └── CommonSettingsApplicationStartupActivity.kt │ │ │ │ ├── compatible/ │ │ │ │ │ └── inspection/ │ │ │ │ │ ├── InspectionProfileService.kt │ │ │ │ │ └── Inspections.kt │ │ │ │ ├── component/ │ │ │ │ │ └── AliProjectComponent.kt │ │ │ │ ├── config/ │ │ │ │ │ ├── P3cConfig.kt │ │ │ │ │ ├── P3cConfigurable.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 │ │ │ │ ├── lifecycle/ │ │ │ │ │ └── P3cPluginLifecycle.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/ │ │ │ ├── activity/ │ │ │ │ └── AliBaseApplicationStartupActivity.kt │ │ │ ├── component/ │ │ │ │ └── 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/ │ │ ├── plugin.xml │ │ └── pluginWithJava.xml │ └── settings.gradle ├── license.txt ├── mvnw ├── mvnw.cmd ├── 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 │ ├── 维护手册.md │ └── 编程规约/ │ ├── OOP规范.md │ ├── 代码格式.md │ ├── 命名风格.md │ ├── 常量定义.md │ ├── 并发处理.md │ ├── 控制语句.md │ ├── 注释规约.md │ └── 集合处理.md ├── p3c-pmd/ │ ├── .gitignore │ ├── .mvn/ │ │ └── wrapper/ │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xenoamess/ │ │ │ └── p3c/ │ │ │ └── pmd/ │ │ │ ├── I18nResources.java │ │ │ ├── config/ │ │ │ │ └── P3cConfigDataBean.java │ │ │ ├── fix/ │ │ │ │ └── FixClassTypeResolver.java │ │ │ └── lang/ │ │ │ ├── AbstractAliXpathRule.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 │ │ │ │ │ │ ├── SwitchExpressionRule.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 │ │ │ │ │ └── xenoamess/ │ │ │ │ │ ├── additional/ │ │ │ │ │ │ ├── EqualsHashCodeRule.java │ │ │ │ │ │ └── SneakyThrowsWithoutExceptionTypeRule.java │ │ │ │ │ └── deprecated/ │ │ │ │ │ └── VarargsParameterRule.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/ │ │ │ └── xenoamess/ │ │ │ └── p3c/ │ │ │ └── pmd/ │ │ │ └── lang/ │ │ │ └── java/ │ │ │ └── rule/ │ │ │ └── concurrent/ │ │ │ └── LockShouldWithTryFinallyRule.kt │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ └── com.xenoamess.p3c.pmd.lang.java.util.namelist.NameListService │ │ ├── messages.xml │ │ ├── messages_en.xml │ │ ├── p3c_config.default.x8l │ │ └── 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 │ │ │ ├── xenoamess-additional.xml │ │ │ └── xenoamess-deprecated.xml │ │ └── vm/ │ │ └── ali-other.xml │ ├── site/ │ │ └── resources/ │ │ └── checkstyle/ │ │ ├── checkstyle-suppressions.xml │ │ └── checkstyle.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── xenoamess/ │ │ └── p3c/ │ │ └── pmd/ │ │ ├── IfVoidIsPrimitiveTest.java │ │ ├── config/ │ │ │ └── TestConfig.java │ │ ├── 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 │ │ │ │ └── xenoamess/ │ │ │ │ ├── additional/ │ │ │ │ │ └── XenoAmessAdditionalTest.java │ │ │ │ └── deprecated/ │ │ │ │ └── XenoAmessDeprecatedTest.java │ │ │ └── vm/ │ │ │ └── rule/ │ │ │ └── other/ │ │ │ └── OtherRulesTest.java │ │ └── testframework/ │ │ ├── ExtendRuleTst.java │ │ └── ExtendSimpleAggregatorTst.java │ └── resources/ │ └── com/ │ └── xenoamess/ │ └── 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 │ │ │ ├── SwitchExpressionRule.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 │ │ └── xenoamess/ │ │ ├── additional/ │ │ │ └── xml/ │ │ │ ├── EqualsHashCodeRule.xml │ │ │ └── SneakyThrowsWithoutExceptionTypeRule.xml │ │ └── deprecated/ │ │ └── xml/ │ │ └── VarargsParameterRule.xml │ └── vm/ │ └── rule/ │ └── other/ │ └── xml/ │ └── UseQuietReferenceNotationRule.xml ├── p3c_config.x8l └── sonar.cmd ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf ================================================ 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: .github/dependabot.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: 2 updates: - package-ecosystem: "maven" directory: "/p3c-pmd/" schedule: interval: "daily" target-branch: "xenoamess_maintain_fork" open-pull-requests-limit: 100 - package-ecosystem: "gradle" directory: "/idea-plugin/" schedule: interval: "daily" target-branch: "xenoamess_maintain_fork" open-pull-requests-limit: 100 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" target-branch: "xenoamess_maintain_fork" open-pull-requests-limit: 100 ================================================ FILE: .github/workflows/auto-merge.yml ================================================ name: Dependabot auto-merge on: pull_request: permissions: pull-requests: write contents: write jobs: dependabot: if: github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest steps: - name: Dependabot metadata id: meta uses: dependabot/fetch-metadata@v2 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Check if auto-merge applicable id: check run: | if [[ "${{ steps.meta.outputs.update-type }}" == "version-update:semver-patch" ]] || \ [[ "${{ steps.meta.outputs.update-type }}" == "version-update:semver-minor" ]]; then echo "should_merge=true" >> "$GITHUB_OUTPUT" else echo "should_merge=false" >> "$GITHUB_OUTPUT" fi - name: Approve dependabot PR if: steps.check.outputs.should_merge == 'true' run: gh pr review --approve "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Enable auto-merge (rebase) if: steps.check.outputs.should_merge == 'true' run: gh pr merge --auto --rebase "$PR_URL" env: PR_URL: ${{ github.event.pull_request.html_url }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/build.yml ================================================ # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI on: [ push ] jobs: build: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} strategy: matrix: os: [ windows-latest , ubuntu-latest , macos-latest ] java: [ 21 ] experimental: [ false ] steps: - uses: actions/checkout@v6 - uses: actions/cache@v5 with: path: ~/.m2 key: ${{ runner.os }}-gradle-m2-${{ hashFiles('**/build.gradle') }} restore-keys: | ${{ runner.os }}-gradle- - uses: actions/cache@v5 with: path: ~/.gradle/caches/ key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/build.gradle') }} restore-keys: | ${{ runner.os }}-gradle- - uses: actions/cache@v5 with: path: ~/.gradle/wrapper/ key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/build.gradle') }} restore-keys: | ${{ runner.os }}-gradle- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: adopt - name: Build Build Build! run: | cd ./p3c-pmd chmod 777 ./mvnw ./mvnw install -Penforce cd ../ cd ./idea-plugin chmod 777 ./gradlew ./gradlew buildPlugin -s - name: Upload Plugin Artifact uses: actions/upload-artifact@v7 with: name: p3c-idea-plugin-${{ matrix.os }}-${{ matrix.java }} path: idea-plugin/p3c-idea/build/distributions/*.zip if-no-files-found: warn ================================================ 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 # tcc_coverage coverage.ec config.client.* temp/ *.pid hsf.configuration/ # code coverage report *.ec #hsf test *.instance out !/p3c-idea/src/main/kotlin/com/alibaba/smartfox/work/tools/aone/ui/AoneBranchView.kt #versions-maven-plugin *.versionsBackup /idea-plugin/.intellijPlatform ================================================ FILE: .mvn/wrapper/MavenWrapperDownloader.java ================================================ /* * Copyright 2007-present 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.net.*; import java.io.*; import java.nio.channels.*; import java.util.Properties; public class MavenWrapperDownloader { private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to * use instead of the default one. */ private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties"; /** * Path where the maven-wrapper.jar will be saved to. */ private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar"; /** * Name of the property which should be used to override the default download url for the wrapper. */ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; public static void main(String args[]) { System.out.println("- Downloader started"); File baseDirectory = new File(args[0]); System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); // If the maven-wrapper.properties exists, read it and check if it contains a custom // wrapperUrl parameter. File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); String url = DEFAULT_DOWNLOAD_URL; if(mavenWrapperPropertyFile.exists()) { FileInputStream mavenWrapperPropertyFileInputStream = null; try { mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); Properties mavenWrapperProperties = new Properties(); mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); } catch (IOException e) { System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); } finally { try { if(mavenWrapperPropertyFileInputStream != null) { mavenWrapperPropertyFileInputStream.close(); } } catch (IOException e) { // Ignore ... } } } System.out.println("- Downloading from: " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); try { downloadFileFromURL(url, outputFile); System.out.println("Done"); System.exit(0); } catch (Throwable e) { System.out.println("- Error downloading"); e.printStackTrace(); System.exit(1); } } private static void downloadFileFromURL(String urlString, File destination) throws Exception { if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { String username = System.getenv("MVNW_USERNAME"); char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); } URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); FileOutputStream fos = new FileOutputStream(destination); fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); fos.close(); rbc.close(); } } ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar ================================================ FILE: AGENTS.md ================================================ # P3C - Alibaba Java Coding Guidelines (XenoAmess TPM) This is a Third-Party Maintenance (TPM) fork of the original [alibaba/p3c](https://github.com/alibaba/p3c) project, maintained by XenoAmess. ## Project Overview P3C is a comprehensive Java coding guidelines implementation that provides: 1. **PMD Rule Implementations** (`p3c-pmd/`) - 54+ rules based on PMD for static code analysis 2. **IntelliJ IDEA Plugin** (`idea-plugin/`) - Real-time code inspection and analysis plugin 3. **Eclipse Plugin** (`eclipse-plugin/`) - Original Eclipse plugin (not actively maintained by TPM) 4. **Code Formatter Configs** (`p3c-formatter/`) - Eclipse code style templates 5. **GitBook Documentation** (`p3c-gitbook/`) - Chinese documentation for the coding guidelines ### Key Features of this TPM - Always compatible with latest JetBrains IDEA releases/EAP versions - Uses latest dependencies versions - Blacklist/Whitelist configuration mechanism for rules/classes - JSON configuration support (alternative to X8L format) - Additional rules beyond original P3C rules ## Project Structure ``` p3c/ ├── p3c-pmd/ # PMD rule implementations (Maven project) │ ├── src/main/java/ # Java rule implementations │ ├── src/main/resources/ # Ruleset XML files and messages │ ├── src/test/java/ # Unit tests for rules │ └── pom.xml # Maven configuration ├── idea-plugin/ # IntelliJ IDEA plugin (Gradle project) │ ├── p3c-common/ # Common plugin code (Kotlin) │ ├── p3c-idea/ # Plugin packaging and resources │ ├── build.gradle # Root Gradle build script │ └── settings.gradle # Gradle project settings ├── eclipse-plugin/ # Eclipse plugin (not actively maintained) ├── p3c-formatter/ # Eclipse code formatter templates ├── p3c-gitbook/ # Documentation in Chinese ├── p3c_config.x8l # Project configuration file (X8L format) ├── build.cmd # Windows build script ├── deploy.cmd # Windows deployment script └── sonar.cmd # SonarQube analysis script ``` ## Technology Stack ### p3c-pmd Module - **Language**: Java 8 - **Build Tool**: Maven 3+ - **Core Dependencies**: - PMD 6.55.0 (pmd-java, pmd-vm) - Kotlin 2.3.10 (stdlib) - X8L 2.3.9 (configuration format) - Commons Lang3 3.20.0 - Commons IO 2.21.0 - Jackson 2.21.1 - **Testing**: JUnit 4.13.2 - **Code Quality**: Checkstyle 11.1.0, PMD Maven Plugin, JaCoCo ### idea-plugin Module - **Language**: Kotlin (primary), Java - **Build Tool**: Gradle with IntelliJ Platform Plugin - **JDK Version**: Java 17 (for building) - **Target IDEA Version**: 2024.1 (configurable via `gradle.properties`) - **Core Dependencies**: - Freemarker 2.3.33 - Javassist 3.30.2-GA - p3c-pmd (local dependency) ## Build Instructions ### Prerequisites - JDK 8+ (for p3c-pmd) - JDK 17+ (for idea-plugin Gradle build) - Maven 3.x ### Building p3c-pmd ```bash cd p3c-pmd ./mvnw clean install -Dmaven.javadoc.skip=false ``` For deployment (requires GPG key): ```bash ./mvnw clean deploy -Dmaven.javadoc.skip=false -Psonatype-oss-release ``` ### Building IDEA Plugin ```bash cd idea-plugin ./gradlew clean buildPlugin ``` To run plugin in development IDE: ```bash ./gradlew runIde ``` To run with specific IDEA version: ```bash ./gradlew runIde -Pidea_version=2024.1 ``` ### Full Build (Windows) Use the provided batch script: ```batch build.cmd ``` ### CI/CD The project uses GitHub Actions (`.github/workflows/build.yml`): - Builds on Windows, Ubuntu, and macOS - Uses Java 17 - Builds both p3c-pmd and idea-plugin - Uploads plugin artifacts ## Code Organization ### PMD Rules Structure Rules are organized by category in `p3c-pmd/src/main/resources/rulesets/java/`: | Ruleset File | Category | |-------------|----------| | `ali-comment.xml` | Code comments and Javadoc | | `ali-concurrent.xml` | Concurrency and threading | | `ali-constant.xml` | Constants conventions | | `ali-exception.xml` | Exception handling | | `ali-flowcontrol.xml` | Flow control statements | | `ali-naming.xml` | Naming conventions | | `ali-oop.xml` | Object-oriented programming | | `ali-orm.xml` | ORM-related rules | | `ali-other.xml` | Other miscellaneous rules | | `ali-set.xml` | Collection usage | | `xenoamess-additional.xml` | Additional TPM-specific rules | | `xenoamess-deprecated.xml` | Deprecated rules | Each rule implementation is located in `p3c-pmd/src/main/java/com/xenoamess/p3c/pmd/lang/java/rule/` following the same category structure. ### Rule Base Classes - `AbstractAliRule` - Base class for all Java rules with type resolution - `AbstractAliXpathRule` - Base class for XPath-based rules - `AbstractPojoRule` - Base class for POJO-related rules - `AbstractAliCommentRule` - Base class for comment-related rules ### Internationalization Message resources are in `p3c-pmd/src/main/resources/`: - `messages.xml` - Chinese (default) - `messages_en.xml` - English ## Configuration Mechanism The project supports flexible configuration via `p3c_config.x8l` (or `p3c_config.json`) file. ### Configuration File Location - For IDEA plugin: `$project_root/p3c_config.x8l` - For Maven PMD: `$mvn_run_directory/p3c_config.x8l` ### Configuration Options ```xml DAOImpl&URL&URI> > > PackageNamingRule&SomeOtherRule> > Console> > com.example.legacy> > SomeRule&AnotherRule> > > ``` ### JSON Alternative Configuration can also be done via JSON format (see `idea-plugin/README.md` for full example). ## Testing ### PMD Rule Tests Tests are located in `p3c-pmd/src/test/java/` following the same package structure as main code. Test resource files (XML format with test cases) are in: `p3c-pmd/src/test/resources/com/xenoamess/p3c/pmd/lang/java/rule//xml/` Run tests: ```bash cd p3c-pmd ./mvnw test ``` ### Test File Format Test XML files contain test cases with code snippets and expected violations: ```xml test description 1 ``` ## Code Style Guidelines ### For Java Code The project enforces code quality via Maven plugins: 1. **Checkstyle**: Configuration in `p3c-pmd/src/site/resources/checkstyle/checkstyle.xml` 2. **PMD**: Self-checking using its own rulesets during build 3. **Animal Sniffer**: Ensures Java 8 compatibility 4. **JaCoCo**: Code coverage reporting Run with enforcement: ```bash ./mvnw clean install -Penforce ``` ### For Kotlin Code - Follow standard Kotlin conventions - Use 4-space indentation - UTF-8 encoding ## Version Information - **Current Version**: 2.3.0 - **Group ID**: `com.xenoamess.p3c` - **Artifact ID**: `p3c-pmd` ## Maven Dependency ```xml com.xenoamess.p3c p3c-pmd 2.3.0 ``` ## Security Considerations 1. The project uses Log4j 2.25.3 (addresses Log4Shell vulnerability) 2. All dependencies are regularly updated via dependabot 3. GPG signing is required for releases ## Contributing - PRs should target `idea-plugin` and `p3c-pmd` modules - Eclipse plugin PRs are not accepted (maintainer has no expertise) - Ensure all tests pass before submitting PR - Follow existing code style and conventions ## License Apache License 2.0 (see `license.txt`) ## Additional Resources - [Alibaba Java Coding Guidelines (Chinese PDF)](Java开发手册(嵩山版).pdf) - [English Documentation](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines) - [JetBrains Plugin Page](https://plugins.jetbrains.com/plugin/14109-alibaba-java-coding-guidelines-xenoamess-tpm-) ================================================ FILE: README.md ================================================ # P3C [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=p3c-idea&metric=alert_status)](https://sonarcloud.io/dashboard?id=p3c-idea) p3c-cmd [![idea plugin](https://sonarcloud.io/api/project_badges/measure?project=p3c-idea&metric=alert_status)](https://sonarcloud.io/dashboard?id=p3c-idea) idea plugin(p3c-common) ## Notice 声明 This Third-party maintenance(TPM) here is forked from original [alibaba/p3c](https://github.com/alibaba/p3c) Follows Apache license described in [license](license.txt) Sources can be found https://github.com/XenoAmess/p3c Releases can be found at https://plugins.jetbrains.com/plugin/14109-alibaba-java-coding-guidelines-xenoamess-tpm- This TPM aims to help maintain alibaba/p3c, fix bugs, and add improvements, as the original developer is too busy to handle them. This TPM is NOT created, or maintained, or controlled by any alibaba employee, in other words it is a TPM, but not an official branch. TPM maintainer XenoAmess have no knowledge with eclipse plugin development. TPM maintainer XenoAmess suggest you only create pr for module idea-plugin and p3c-pmd, unless you really have a strong reason. ## Features 特性 1. This TPM can always run on latest Jetbrains-idea release/EAP. 本第三方维护版会在任何 Jetbrains-idea release 或者EAP更新时保证可用。 2. This TPM will always use as latest dependencies as possible. 本第三方维护版会使用尽可能新的依赖版本。 3. This TPM implements a mechanism for configuring black-list/white-list for rules/classes 本第三方维护版实现有一套配置系统,可以对源码类/规则进行黑名单/白名单过滤。 4. This TPM does NOT fully obey p3c rules. Especially for the rule who disable deprecated functions. Maintainer XenoAmess will make sure they are usable, but have no enough time to eliminate every deprecated functions. 本第三方维护版不完全遵循p3c,尤其是关于禁止使用deprecated函数的规则。 维护者XenoAmess会保证函数可用,但是没有充裕的时间保证消灭每一个deprecated函数。 ## 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*: - 中文版: *[阿里巴巴Java开发手册](https://github.com/alibaba/p3c/blob/master/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E5%B5%A9%E5%B1%B1%E7%89%88%EF%BC%89.pdf)* - English Version: *[Alibaba Java Coding Guidelines](https://alibaba.github.io/Alibaba-Java-Coding-Guidelines)* - 《阿里巴巴Java开发手册》书籍版天猫官方店: *[阿里巴巴Java开发手册最新版](https://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.1.4577601asIhzbW&id=628337143778&areaId=330100&user_id=1932014659&cat_id=2&is_b=1&rn=11a322ef70720cdf5e894001e4b48b55)* - 《码出高效》书籍版天猫官方店: *[码出高效:Java开发手册](https://detail.tmall.com/item.htm?spm=a230r.1.14.40.7dee7d6bwpO82U&id=575107529181&ns=1&abbucket=20)* ## 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. ## Config Mechanism 配置机制 see [documents](idea-plugin/README.md) [说明](idea-plugin/README_cn.md) ================================================ FILE: build.cmd ================================================ set JAVA_HOME=C:\jdk-21\ cd ./p3c-pmd call ./mvnw clean install -Dmaven.javadoc.skip=false -e -X cd ../ cd ./idea-plugin set JAVA_HOME=C:\jdk-21\ cd ./p3c-common call ../gradlew clean publishToMavenLocal cd ../ cd ./p3c-idea call ../gradlew clean publishToMavenLocal buildPlugin cd ../ cd ../ ================================================ FILE: deploy.cmd ================================================ set JAVA_HOME=C:\jdk-8\ cd ./p3c-pmd call ./mvnw clean deploy -Dmaven.javadoc.skip=false -e -X -Psonatype-oss-release cd ../ cd ./idea-plugin set JAVA_HOME=C:\jdk-21\ cd ./p3c-common rem call ../gradlew clean install sign uploadArchives -DossrhUsername="%ossrhUsername%" -DossrhPassword="%ossrhPassword%" call ../gradlew publishToMavenLocal cd ../ cd ./p3c-idea call ../gradlew clean publishToMavenLocal buildPlugin cd ../ cd ../ ================================================ 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 # tcc_coverage coverage.ec config.client.* temp/ *.pid hsf.configuration/ # code coverage report *.ec #hsf test *.instance out **/idea-sandbox ================================================ FILE: idea-plugin/README.md ================================================ # Idea Plugin --- ## Prepare - Project JDK: 11 - Gradle: 7.3(Require JDK17 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.xenoamess.p3c.idea:p3c-common:2.3.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) Results are grouped by tags, which include Blocker,Critical,Major,Warning,Weak Warning. Blocker, Critical and Major will remain sync with alibaba official version, and will not be changed freely, unless especially declared. When I see some reasonable third party rules in Community I might add them to Warning or Weak Warning. Usually, if a third party rule can be followed in every situation, then it is tagged Warning. Otherwise, if it cannot be followed in some special cases, then it will be tagged Weak Warning. If you want to use pmd-maven-plugin for auto-analyze and assure, You should set `3` for having same behavior than alibaba official version. See example/use-case in `p3c-pmd/pom.xml` 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) ## Configuration mechanism ### Configuration file path For p3c-idea-plugin, configuration file should be put at "$your_project_path/p3c_config.x8l". For p3c-pmd for maven, configuration file should be put at "$the_dir_you_run_mvn/p3c_config.x8l". Usually this two position be pointed at a same file. ### File format File format be [x8l](https://github.com/cyanpotion/x8l). Yep I write it. ### File analyze For example at [cyan_potion](https://github.com/cyanpotion/cyan_potionp3c_config.x8l) , we learn about how it works. Firstly p3c_config.x8l be at "$your_project_path/p3c_config.x8l". ``` DAOImpl& GLFW& URL& URI& XInput& PosX& PosY& AWT& XY& drawBoxTC& FPS& ID& lastX& lastY& > > Hbase& HBase& ID& ConcurrentHashMap& GLFW& URL& URI& JXInput& SettingFileParser_ > > > PackageNamingRule& AbstractClassShouldStartWithAbstractNamingRule& ThreadPoolCreationRule& MethodTooLongRule& > Console > com.xenoamess.cyan_potion.base.steam > EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> UndefineMagicConstantRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> LowerCamelCaseVariableNamingRule&AvoidUseDeprecationRule> ConstantFieldShouldBeUpperCaseRule> > > ``` Root node's name MUST be "com.alibaba.p3c.pmd.config". Attribute "version=0.0.1" is version of configuration file. It is actually not used yet but stongly suggested force it here, as for breaking updates. For this repo, we opened trim for x8l file, means all readed String will be trimed first. For example, ``` Console > ``` see the TextNode ``` Console ``` It will be trimmed to `Console` before used. There can be 4 children nodes under node com.alibaba.p3c.pmd.config, as shown below. ### rule_config Node rule_config contains detailed settings for some rules. For example, LowerCamelCaseVariableNamingRule's attribute WHITE_LIST. This attribute need a String List, then we read several TextNodes under a ContentNode as a String List. ### rule_blacklist Node rule_blacklist contains global settings for a repo, means ban rules in this repo globally. For example, if rule_blacklist contains PackageNamingRule, then means in this repo we will not detect PackageNamingRule. Rule class name in rule_blacklist CAN be SimpleName OR CanonicalName. ### class_blacklist Node class_blacklist contains global settings for a repo, means ban classes in this repo globally. For example, if class_blacklist contains Console, then means in this repo we will not detect anything for all classes whose name be Console. BE ATTENTION, according to PMD interface reason, class names in class_blacklist must be SimpleName. ### package_blacklist Node package_blacklist contains global settings for a repo, means ban packages in this repo globally. For example, if package_blacklist contains `com.xenoamess`, then means in this repo we will not detect anything for all classes whose package under `com.xenoamess`. BE ATTENTION, it will also ban packages under `com.xenoamess`, like `com.xenoamess.cyan_potion` ### rule_class_pair_blacklist Node rule_class_pair_blacklist contains settings for class/rule pairs, means ban some rules in this repo for some classes. For example, if rule_class_pair_blacklist contains `LowerCamelCaseVariableNamingRule&AvoidUseDeprecationRule>` , then means in this repo we will not detect LowerCamelCaseVariableNamingRule nor AvoidUseDeprecationRule, for all classes whose name be GameInputManager. Rule class name in rule_class_pair_blacklist CAN be SimpleName OR CanonicalName. BE ATTENTION, according to PMD interface reason, class names in rule_class_pair_blacklist must be SimpleName. ### For the X8L Haters If you really hate x8l you can use json configuration files. ```json { "com.alibaba.p3c.pmd.config": { "_attributes": { "version": "0.0.1" }, "rule_config": { "LowerCamelCaseVariableNamingRule": { "WHITE_LIST": [ "DAOImpl", "GLFW", "URL", "URI", "XInput", "PosX", "PosY", "AWT", "XY", "drawBoxTC", "FPS", "ID", "lastX", "lastY" ] }, "ClassNamingShouldBeCamelRule": { "CLASS_NAMING_WHITE_LIST": [ "Hbase", "HBase", "ID", "ConcurrentHashMap", "GLFW", "URL", "URI", "JXInput", "SettingFileParser_" ] } }, "rule_blacklist": [ "PackageNamingRule", "AbstractClassShouldStartWithAbstractNamingRule", "ThreadPoolCreationRule", "MethodTooLongRule" ], "class_blacklist": [ "Console" ], "package_blacklist": [ "com.xenoamess.cyan_potion.base.steam" ], "rule_class_pair_blacklist": { "JamepadGamepadKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "JXInputGamepadKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "KeyActionEnum": [ "EnumConstantsMustHaveCommentRule" ], "KeyboardKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "CodePluginPosition": [ "EnumConstantsMustHaveCommentRule" ], "ShapeRelation": [ "EnumConstantsMustHaveCommentRule" ], "WaveData": [ "UndefineMagicConstantRule" ], "FileUtils": [ "AvoidUseDeprecationRule" ], "Font": [ "AvoidUseDeprecationRule" ], "Keymap": [ "AvoidUseDeprecationRule" ], "WorldForDemo": [ "AvoidUseDeprecationRule" ], "GameInputManager": [ "LowerCamelCaseVariableNamingRule", "AvoidUseDeprecationRule" ], "Colors": [ "ConstantFieldShouldBeUpperCaseRule" ] } } } ``` name it p3c_config.json ================================================ 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、Warning、Weak Warning。默认按等级分组,方便统计每个级别错误的数量。 ![](https://gw.alicdn.com/tfscom/TB1aC1yifJNTKJjSspoXXc6mpXa.png) 其中,Blocker、Critical、Major三个级别保持与阿里巴巴官方版相同,不会轻易改变。如果未来需要改变,则必须在此Readme中注明。 看见有人做了比较有道理的第三方规则的话,会是视情况加入Warning或者Weak Warning。 如无例外,一个第三方规则如果在任何情况下都能够执行,即程序员完全可以实现不违背该规则实现任何功能,则这个规则会被放入Warning。 与之相对的,如果存在特定的条件,使得若需要满足特定条件则必须违背该规则,即程序员存在必须违背该规则的场景,则这个规则会被放入Weak Warning。 如果要使用pmd maven plugin对代码进行自动检查以确保不违反阿里巴巴Java开发手册, 则请设置`3`,以保证与官方版本规则吻合。 使用例可见`p3c-pmd/pom.xml` 默认情况我们在结果面板需要双击具体违规项才能打开对应的源文件,开启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) ## 设置文件机制 ### 文件目录 对于p3c idea插件来说,配置文件存放的位置为项目根目录下的p3c_config.x8l文件。 对于maven项目来说,配置文件存放的位置为运行mvn的目录下的p3c_config.x8l文件。 经常的,这两个位置指向同一个位置。 ### 文件格式 文件格式为 [x8l](https://github.com/cyanpotion/x8l). ### 文件描述 举个例子,以 [cyan_potion](https://github.com/cyanpotion/cyan_potionp3c_config.x8l) 为例, 我们来看如何配置p3c_config.x8l 首先,p3c_config.x8l位于项目根目录。 ``` DAOImpl& GLFW& URL& URI& XInput& PosX& PosY& AWT& XY& drawBoxTC& FPS& ID& lastX& lastY& > > Hbase& HBase& ID& ConcurrentHashMap& GLFW& URL& URI& JXInput& SettingFileParser_ > > > PackageNamingRule& AbstractClassShouldStartWithAbstractNamingRule& ThreadPoolCreationRule& MethodTooLongRule& > Console > com.xenoamess.cyan_potion.base.steam > EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> EnumConstantsMustHaveCommentRule> UndefineMagicConstantRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> AvoidUseDeprecationRule> LowerCamelCaseVariableNamingRule&AvoidUseDeprecationRule> ConstantFieldShouldBeUpperCaseRule> > > ``` 其中,根节点名必为com.alibaba.p3c.pmd.config 。 属性version=0.0.1为配置文件version,目前尚未实际应用,但是建议写明,以防后续破坏性更新。 对于这个项目,方便/美观起见,我们开启了trim,所有读取的String会被trim,然后再进行处理。 例如, ``` Console > ``` 中的TextNode ``` Console ``` 会被trim为`Console`然后使用。 com.alibaba.p3c.pmd.config节点下有四个子节点,以下依次讲解。 ### rule_config rule_config节点主要包括对于提供了设置属性的规则的具体设置。 如,LowerCamelCaseVariableNamingRule的WHITE_LIST属性。 该属性要求提供一字符串List,则本项目中读取一个ContentNode下多个TextNode转换为字符串List。 ### rule_blacklist rule_blacklist节点主要包括对该项目的所有类禁用某一规则。 如,rule_blacklist中含有PackageNamingRule,则表明该项目中,检测器不会检测PackageNamingRule。 rule_blacklist中的Rule类名既可以是SimpleName,也可以是CanonicalName。 ### class_blacklist class_blacklist节点主要包括对该项目中的某一类禁用所有规则。 如,class_blacklist中含有Console类,则该项目中所有名为Console的类均不会进行任何检测。 注意,由于PMD技术原因,class_blacklist中的类必须为SimpleName ### package_blacklist package_blacklist节点主要包括对该项目中的某一个父包禁用所有规则。 如,package_blacklist中含有`com.xenoamess`包,则该项目中所有`com.xenoamess`包下的类均不会进行任何检测。 注意,形如`com.xenoamess.cyan_potion`的,`com.xenoamess`包的子包也会被禁用所有规则。 ### rule_class_pair_blacklist rule_class_pair_blacklist节点主要包括对该项目中的某一类禁用某数个规则。 如,rule_class_pair_blacklist中含有 `LowerCamelCaseVariableNamingRule&AvoidUseDeprecationRule>` , 则该项目中所有名为GameInputManager的类中,LowerCamelCaseVariableNamingRule与AvoidUseDeprecationRule不会被检测。 Rule类名既可以是SimpleName,也可以是CanonicalName。 注意,由于PMD技术原因,被忽略检测的类名必须为SimpleName。 ### 为了反X8L主义者干杯 如果你确实讨厌X8L文件格式,你可以使用等价json格式配置文件。 ```json { "com.alibaba.p3c.pmd.config": { "_attributes": { "version": "0.0.1" }, "rule_config": { "LowerCamelCaseVariableNamingRule": { "WHITE_LIST": [ "DAOImpl", "GLFW", "URL", "URI", "XInput", "PosX", "PosY", "AWT", "XY", "drawBoxTC", "FPS", "ID", "lastX", "lastY" ] }, "ClassNamingShouldBeCamelRule": { "CLASS_NAMING_WHITE_LIST": [ "Hbase", "HBase", "ID", "ConcurrentHashMap", "GLFW", "URL", "URI", "JXInput", "SettingFileParser_" ] } }, "rule_blacklist": [ "PackageNamingRule", "AbstractClassShouldStartWithAbstractNamingRule", "ThreadPoolCreationRule", "MethodTooLongRule" ], "class_blacklist": [ "Console" ], "package_blacklist": [ "com.xenoamess.cyan_potion.base.steam" ], "rule_class_pair_blacklist": { "JamepadGamepadKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "JXInputGamepadKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "KeyActionEnum": [ "EnumConstantsMustHaveCommentRule" ], "KeyboardKeyEnum": [ "EnumConstantsMustHaveCommentRule" ], "CodePluginPosition": [ "EnumConstantsMustHaveCommentRule" ], "ShapeRelation": [ "EnumConstantsMustHaveCommentRule" ], "WaveData": [ "UndefineMagicConstantRule" ], "FileUtils": [ "AvoidUseDeprecationRule" ], "Font": [ "AvoidUseDeprecationRule" ], "Keymap": [ "AvoidUseDeprecationRule" ], "WorldForDemo": [ "AvoidUseDeprecationRule" ], "GameInputManager": [ "LowerCamelCaseVariableNamingRule", "AvoidUseDeprecationRule" ], "Colors": [ "ConstantFieldShouldBeUpperCaseRule" ] } } } ``` 文件名为p3c_config.json即可。 ================================================ FILE: idea-plugin/build.gradle ================================================ static boolean shouldIncludeJava(String idea_version_string) { if (idea_version_string.contains('-')) { idea_version_string = idea_version_string.substring(idea_version_string.indexOf('-') + 1) } if (idea_version_string.contains('.')) { idea_version_string = idea_version_string.substring(0, idea_version_string.indexOf('.')) } try { int idea_version_int = Integer.parseInt(idea_version_string); return idea_version_int >= 2019 || (idea_version_int < 1500 && idea_version_int >= 193); } catch (Exception e) { return true } } buildscript { project.ext { kotlin_version = "2.3.21" } repositories { mavenCentral() mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://plugins.gradle.org/m2/" } gradlePluginPortal() google() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$project.kotlin_version" } } allprojects { group 'com.xenoamess.p3c.idea' apply plugin: 'kotlin' apply plugin: 'maven-publish' configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } repositories { mavenCentral() mavenLocal() maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://plugins.gradle.org/m2/" } gradlePluginPortal() google() } } subprojects { apply plugin: 'java' java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } compileJava.options.encoding = 'UTF-8' kotlin { jvmToolchain(21) } } ================================================ FILE: idea-plugin/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: idea-plugin/gradle.properties ================================================ # See https://github.com/JetBrains/gradle-intellij-plugin/ # See https://www.jetbrains.org/intellij/sdk/docs/reference_guide/intellij_artifacts.html # See https://www.jetbrains.com/intellij-repository/releases/ # See https://www.jetbrains.com/intellij-repository/snapshots/ #idea_version=2022.1 idea_version=2025.1 #idea_version=181.5540.23 #idea_version=145.258.11 plugin_name=Alibaba Java Coding Guidelines(XenoAmess TPM) systemProp.file.encoding=UTF-8 plugin_version=2.3.0 org.gradle.jvmargs='-Dfile.encoding=UTF-8' ================================================ FILE: idea-plugin/gradlew ================================================ #!/bin/sh # # Copyright © 2015 the original 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. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # 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 ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac # 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 if ! command -v java >/dev/null 2>&1 then 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 fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # 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"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' 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 @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables, and ensure extensions are enabled setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused 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% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 "%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 "%COMSPEC%" /c exit 1 :execute @rem Setup the command line @rem Execute Gradle @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel :exitWithErrorLevel @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts "%COMSPEC%" /c exit %ERRORLEVEL% ================================================ FILE: idea-plugin/p3c-common/build.gradle ================================================ plugins { id 'java' id("org.jetbrains.intellij.platform") version "2.16.0" id "org.sonarqube" version "6.3.1.5724" id "com.github.ben-manes.versions" version "0.54.0" } repositories { mavenCentral() intellijPlatform { defaultRepositories() } } apply plugin: 'kotlin' apply plugin: 'idea' apply plugin: 'signing' javadoc { options.tags = ["date"] } task javadocJar(type: Jar, dependsOn: javadoc) { archiveClassifier.convention('javadoc'); archiveClassifier.set('javadoc'); from 'build/docs/javadoc' } task sourcesJar(type: Jar) { archiveClassifier.convention('sources'); archiveClassifier.set('sources'); from sourceSets.main.allSource } artifacts { archives jar archives javadocJar archives sourcesJar } sonarqube { properties { property "sonar.projectKey", "p3c-idea" property "sonar.organization", "xenoamess-github" property "sonar.host.url", "https://sonarcloud.io" property "sonar.login", "023904832cfec46a1aa61bb7aeee156b066cb3bc" } } intellijPlatform { pluginConfiguration { name = plugin_name } buildSearchableOptions = false } dependencies { intellijPlatform { create("IC", idea_version) bundledPlugins(['com.intellij.java']) } implementation group: 'org.freemarker', name: 'freemarker', version: '2.3.33' implementation("com.xenoamess.p3c:p3c-pmd:$plugin_version") { exclude group: 'org.apache.logging.log4j' exclude group: 'org.slf4j' } implementation group: 'org.javassist', name: 'javassist', version: '3.31.0-GA' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$project.kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$project.kotlin_version" testImplementation group: 'junit', name: 'junit', version: '4.13.2' } version plugin_version ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 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", P3cIcons.class); /** * qiyong means 启用 */ public static final Icon PROJECT_INSPECTION_ON = IconLoader.getIcon("/icons/qiyong.png", P3cIcons.class); /** * tingyong means 停用 */ public static final Icon PROJECT_INSPECTION_OFF = IconLoader.getIcon("/icons/tingyong.png", P3cIcons.class); public static final Icon LANGUAGE = IconLoader.getIcon("/icons/language.png", P3cIcons.class); public static final Icon ALIBABA = IconLoader.getIcon("/icons/alibaba.png", P3cIcons.class); } ================================================ 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.analysis.problemsView.toolWindow.ProblemsView 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.* 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.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 = project.getService(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, BaseAnalysisActionDialog.standardItems( project, analysisScope, module, psiElement ), uiOptions, true ) if (!dialog.showAndGet()) { return } val scope = dialog.getScope(analysisScope) 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 = ProblemsView.getToolWindow(project); 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) } } override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.BGT } } ================================================ 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?.lowercase() ?: "" 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) { ApplicationManager.getApplication().runReadAction( Runnable { 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 { if (ApplicationManager.getApplication().isReadAccessAllowed) { outFilesToInspect.put(psiFile) } } catch (e: InterruptedException) { logger.error("psiFile interrupted : " + psiFile.name, e) } catch (e: IllegalStateException) { logger.info("psiFile ignored : " + psiFile.name, 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.ActionUpdateThread 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}") } override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.EDT } } ================================================ 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.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.psi.PsiManager import icons.P3cIcons /** * * Open or close inspections * Enhanced for dynamic plugin support - allows toggling plugin without IDE restart * * @author caikang * @date 2017/03/14 */ class ToggleProjectInspectionAction : AnAction() { val textKey = "com.alibaba.p3c.idea.action.ToggleProjectInspectionAction.text" override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return val smartFoxConfig = project.getService(SmartFoxProjectConfig::class.java) // Toggle global plugin state smartFoxConfig.pluginGloballyDisabled = !smartFoxConfig.pluginGloballyDisabled // Apply the state change applyPluginState(project, !smartFoxConfig.pluginGloballyDisabled) // Show notification to user val message = if (smartFoxConfig.pluginGloballyDisabled) { P3cBundle.getMessage("com.alibaba.p3c.idea.action.ToggleProjectInspectionAction.disabled.notification") ?: "P3C plugin has been disabled" } else { P3cBundle.getMessage("com.alibaba.p3c.idea.action.ToggleProjectInspectionAction.enabled.notification") ?: "P3C plugin has been enabled" } NotificationGroupManager.getInstance() .getNotificationGroup("P3C Notifications") ?.createNotification(message, NotificationType.INFORMATION) ?.notify(project) } /** * Apply plugin enabled/disabled state */ private fun applyPluginState(project: com.intellij.openapi.project.Project, enable: Boolean) { val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } // Use the existing toggle mechanism InspectionProfileService.toggleInspection(project, tools, !enable) // Refresh all open files to apply changes immediately FileEditorManager.getInstance(project).openFiles.forEach { file -> val psiFile = PsiManager.getInstance(project).findFile(file) if (psiFile != null) { DaemonCodeAnalyzer.getInstance(project).restart(psiFile) } } } override fun update(e: AnActionEvent) { val project = e.project ?: return val smartFoxConfig = project.getService(SmartFoxProjectConfig::class.java) // Check global disabled state first, fall back to project level val isDisabled = smartFoxConfig.pluginGloballyDisabled || smartFoxConfig.projectInspectionClosed e.presentation.text = if (isDisabled) { e.presentation.icon = P3cIcons.PROJECT_INSPECTION_ON P3cBundle.getMessage("$textKey.open") ?: "Enable P3C Inspections" } else { e.presentation.icon = P3cIcons.PROJECT_INSPECTION_OFF P3cBundle.getMessage("$textKey.close") ?: "Disable P3C Inspections" } } override fun getActionUpdateThread(): ActionUpdateThread { return ActionUpdateThread.BGT } } ================================================ FILE: idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/activity/CommonSettingsApplicationStartupActivity.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.activity import com.alibaba.p3c.idea.config.P3cConfig import com.alibaba.p3c.idea.i18n.P3cBundle import com.alibaba.p3c.idea.util.HighlightInfoTypes import com.alibaba.p3c.idea.util.HighlightSeverities import com.xenoamess.p3c.pmd.I18nResources import com.alibaba.smartfox.idea.common.activity.AliBaseApplicationStartupActivity import com.intellij.codeInsight.daemon.impl.SeverityRegistrar import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/06/19 */ class CommonSettingsApplicationStartupActivity : AliBaseApplicationStartupActivity { companion object { const val analyticsGroupId = "com.alibaba.p3c.analytics.action_group" val analyticsGroupText = "$analyticsGroupId.text" private val p3cConfig = P3cConfig() } override fun runActivity(project: Project) { SeverityRegistrar.registerStandard(HighlightInfoTypes.BLOCKER, HighlightSeverities.BLOCKER) SeverityRegistrar.registerStandard(HighlightInfoTypes.CRITICAL, HighlightSeverities.CRITICAL) SeverityRegistrar.registerStandard(HighlightInfoTypes.MAJOR, HighlightSeverities.MAJOR) // Shall not register WARNING and WEAK_WARNING! // SeverityRegistrar.registerStandard(HighlightInfoTypes.WARNING, HighlightSeverities.WARNING) // SeverityRegistrar.registerStandard(HighlightInfoTypes.WEAK_WARNING, HighlightSeverities.WEAK_WARNING) I18nResources.changeLanguage(p3cConfig.locale) val analyticsGroup = ActionManager.getInstance().getAction(analyticsGroupId) analyticsGroup.templatePresentation.text = P3cBundle.getMessage(analyticsGroupText) } } ================================================ 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).currentProfile } } ================================================ 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.xenoamess.p3c.pmd.lang.java.util.namelist.NameListConfig 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.openapi.project.Project import com.intellij.openapi.util.JDOMExternalizerUtil import com.xenoamess.p3c.pmd.lang.java.util.namelist.NameListServiceImpl.P3C_JSON_CONFIG_FILE_NAME import com.xenoamess.p3c.pmd.lang.java.util.namelist.NameListServiceImpl.P3C_X8L_CONFIG_FILE_NAME import org.apache.commons.lang3.StringUtils import org.jdom.Element import org.jetbrains.annotations.SystemIndependent import java.io.File import java.io.FilenameFilter /** * @author XenoAmess */ class P3cConfigFilenameFilter : FilenameFilter { override fun accept(dir: File?, name: String?): Boolean { return P3C_X8L_CONFIG_FILE_NAME.equals(name) || P3C_JSON_CONFIG_FILE_NAME.equals(name) } } /** * * * @author caikang * @date 2017/03/01 */ object Inspections { fun aliInspections(project: Project, filter: (InspectionToolWrapper<*, *>) -> Boolean): List> { loadPatchConfigFile(project) val profile = InspectionProfileService.getProjectInspectionProfile(project) return getAllTools(project, profile).filter(filter) } fun loadPatchConfigFile(project: Project) { val patchConfigFile = fetchPatchConfigFile(project) if (patchConfigFile == null) { NameListConfig.renewNameListService() } else { NameListConfig.renewNameListService(patchConfigFile) } } private fun fetchPatchConfigFile(project: Project): File? { val projectBasePath: @SystemIndependent String? = project.basePath ?: return null val projectBaseFile = File(projectBasePath) val fileList = projectBaseFile.listFiles(P3cConfigFilenameFilter()) return if (fileList == null || fileList.isEmpty()) { null } else { fileList[0] } } fun addCustomTag(project: Project, tag: String) { try { val profile = InspectionProfileService.getProjectInspectionProfile(project) val javaDocLocalInspection : Element = profile.getInspectionTool("JavaDoc", project)?.tool as? Element ?: return val myAdditionalJavadocTags : String? = JDOMExternalizerUtil.readField(javaDocLocalInspection, "myAdditionalJavadocTags") if (StringUtils.isEmpty(myAdditionalJavadocTags)) { JDOMExternalizerUtil.writeField(javaDocLocalInspection, "myAdditionalJavadocTags", tag) return } val tags = Splitter.on(',').splitToList(myAdditionalJavadocTags) if (tags.contains(tag)) { return } JDOMExternalizerUtil.writeField(javaDocLocalInspection, "myAdditionalJavadocTags", myAdditionalJavadocTags + ",$tag") profile.profileChanged() profile.scopesChanged() } catch (ignored : Exception) { } } 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.smartfox.idea.common.component.AliBaseProjectComponent import com.alibaba.smartfox.idea.common.util.getService import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.vfs.VirtualFile 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 com.xenoamess.p3c.pmd.I18nResources import net.sourceforge.pmd.RuleViolation import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.locks.ReentrantReadWriteLock /** * @author caikang * @date 2016/12/13 */ class AliProjectComponent : 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 : VirtualFileListener { override fun contentsChanged(event: VirtualFileEvent) { val path = getFilePath(event) ?: return val project = ProjectManager.getInstance().defaultProject PsiManager.getInstance(project).findFile(event.file) ?: return val p3cConfig = P3cConfig::class.java.getService() 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() { val p3cConfig = P3cConfig::class.java.getService() I18nResources.changeLanguage(p3cConfig.locale) val analyticsGroup = ActionManager.getInstance().getAction(analyticsGroupId) analyticsGroup.templatePresentation.text = P3cBundle.getMessage(analyticsGroupText) } override fun projectOpened() { val project = ProjectManager.getInstance().defaultProject 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 } } } /** * Get the virtual file listener for cleanup purposes. */ fun getVirtualFileListener(): VirtualFileListener = listener } ================================================ 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/P3cConfigurable.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.alibaba.p3c.idea.compatible.inspection.InspectionProfileService import com.alibaba.p3c.idea.compatible.inspection.Inspections import com.alibaba.p3c.idea.inspection.AliBaseInspection import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.openapi.options.Configurable import com.intellij.openapi.project.Project import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.psi.PsiManager import java.awt.BorderLayout import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.Insets import javax.swing.BorderFactory import javax.swing.JCheckBox import javax.swing.JComponent import javax.swing.JLabel import javax.swing.JPanel /** * Settings page for P3C plugin configuration. * Provides GUI for enabling/disabling plugin without IDE restart. * * @author XenoAmess */ class P3cConfigurable(private val project: Project) : Configurable { private var mainPanel: JPanel? = null private var enablePluginCheckBox: JCheckBox? = null private var enableRealtimeCheckBox: JCheckBox? = null override fun getDisplayName(): String = "P3C Code Guidelines" override fun createComponent(): JComponent? { mainPanel = JPanel(BorderLayout(10, 10)) mainPanel?.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) val settingsPanel = JPanel(GridBagLayout()) val gbc = GridBagConstraints().apply { fill = GridBagConstraints.HORIZONTAL insets = Insets(5, 5, 5, 5) weightx = 1.0 gridx = 0 } // Plugin enable/disable checkbox enablePluginCheckBox = JCheckBox("Enable P3C Code Guidelines plugin") enablePluginCheckBox?.toolTipText = "Enable or disable all P3C inspections without IDE restart" gbc.gridy = 0 gbc.gridwidth = 2 settingsPanel.add(enablePluginCheckBox, gbc) // Description label val descLabel = JLabel("" + "When disabled, all P3C inspections will be turned off immediately. " + "No IDE restart is required for this change to take effect." + "") descLabel.font = descLabel.font.deriveFont(descLabel.font.size2D - 1) gbc.gridy = 1 gbc.insets = Insets(0, 25, 15, 5) settingsPanel.add(descLabel, gbc) // Real-time inspection checkbox enableRealtimeCheckBox = JCheckBox("Enable real-time inspection") enableRealtimeCheckBox?.toolTipText = "Enable or disable real-time code inspection as you type" gbc.gridy = 2 gbc.insets = Insets(5, 5, 5, 5) settingsPanel.add(enableRealtimeCheckBox, gbc) // Real-time description label val realtimeDescLabel = JLabel("" + "When enabled, P3C inspections run automatically while editing. " + "Disabling this can improve editor performance on large files." + "") realtimeDescLabel.font = realtimeDescLabel.font.deriveFont(realtimeDescLabel.font.size2D - 1) gbc.gridy = 3 gbc.insets = Insets(0, 25, 5, 5) settingsPanel.add(realtimeDescLabel, gbc) // Fill remaining space gbc.gridy = 4 gbc.weighty = 1.0 gbc.fill = GridBagConstraints.BOTH settingsPanel.add(JPanel(), gbc) mainPanel?.add(settingsPanel, BorderLayout.NORTH) reset() return mainPanel } override fun isModified(): Boolean { val config = project.getService(SmartFoxProjectConfig::class.java) val pluginEnabled = !config.pluginGloballyDisabled val realtimeEnabled = config.realtimeInspectionEnabled return enablePluginCheckBox?.isSelected != pluginEnabled || enableRealtimeCheckBox?.isSelected != realtimeEnabled } override fun apply() { val config = project.getService(SmartFoxProjectConfig::class.java) val wasGloballyDisabled = config.pluginGloballyDisabled val pluginEnabled = enablePluginCheckBox?.isSelected ?: true config.pluginGloballyDisabled = !pluginEnabled val wasRealtimeEnabled = config.realtimeInspectionEnabled config.realtimeInspectionEnabled = enableRealtimeCheckBox?.isSelected ?: true // Apply changes immediately if state changed if (wasGloballyDisabled != config.pluginGloballyDisabled) { applyPluginState(project, !config.pluginGloballyDisabled) } // Refresh all open files to apply changes if (wasGloballyDisabled != config.pluginGloballyDisabled || wasRealtimeEnabled != config.realtimeInspectionEnabled) { FileEditorManager.getInstance(project).openFiles.forEach { file -> val psiFile = PsiManager.getInstance(project).findFile(file) if (psiFile != null) { DaemonCodeAnalyzer.getInstance(project).restart(psiFile) } } } } override fun reset() { val config = project.getService(SmartFoxProjectConfig::class.java) enablePluginCheckBox?.isSelected = !config.pluginGloballyDisabled enableRealtimeCheckBox?.isSelected = config.realtimeInspectionEnabled } /** * Apply plugin enabled/disabled state */ private fun applyPluginState(project: Project, enable: Boolean) { val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } InspectionProfileService.toggleInspection(project, tools, !enable) } } ================================================ 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 /** * Global plugin disable state for dynamic plugin support. * When true, all P3C inspections are completely disabled without IDE restart. */ var pluginGloballyDisabled = false /** * Individual rule disable list. * Contains short names of disabled rules. */ var disabledRules: MutableSet = mutableSetOf() /** * Enable/disable real-time inspection. */ var realtimeInspectionEnabled = true 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.xenoamess.p3c.pmd.I18nResources import com.alibaba.smartfox.idea.common.util.getService import com.intellij.AbstractBundle import com.intellij.openapi.application.CachedSingletonsRegistry import java.util.Locale import java.util.ResourceBundle import java.util.function.Supplier /** * * * @author caikang * @date 2017/06/20 */ object P3cBundle { private val p3cConfigSupplier: Supplier = CachedSingletonsRegistry.lazy { P3cConfig::class.java.getService() } private val resourceBundleSupplier = CachedSingletonsRegistry.lazy { ResourceBundle.getBundle( "messages.P3cBundle", Locale(p3cConfigSupplier.get().locale), I18nResources.XmlControl() ) } fun getMessage(key: String): String { return resourceBundleSupplier.get().getString(key).trim() } fun message(key: String, vararg params: Any): String { return AbstractBundle.message(resourceBundleSupplier.get(), 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 short 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 { const 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.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 com.xenoamess.p3c.pmd.I18nResources 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.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.setLanguageSupplier( { 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[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: Exception) { LOGGER.error(String.format("rule set %s load failed 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.config.SmartFoxProjectConfig 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.openapi.components.ServiceManager 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? { // Check if plugin is globally disabled (for dynamic plugin support) val config = ServiceManager.getService(SmartFoxProjectConfig::class.java) if (config == null) { return null } if (config.pluginGloballyDisabled) { return null } // Check if real-time inspection is disabled (for on-the-fly checks) if (isOnTheFly && !config.realtimeInspectionEnabled) { return null } // Check individual rule disable list if (config.disabledRules.contains(getShortName())) { return null } 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.xenoamess.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()!! } /** * Clear all file violations cache for dynamic plugin unload. */ fun clearAllFileViolationsCache() { if (::invokers.isInitialized) { invokers.invalidateAll() } } } } 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() fun familyName(): String { return 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 { return if (deepEquals) { replaceWith() + " 'Arrays.deepEquals()'" } else { 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 { @JvmStatic fun replaceWith(): String { return 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.xenoamess.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 const val ruleSetsPrefix = "rulesets/" private val ruleStaticDescriptions: Map private val ruleMessages: Map private val displayLevelMap: Map init { I18nResources.setLanguageSupplier( { 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 { return when (rulePriority) { RulePriority.HIGH -> HighlightDisplayLevels.BLOCKER RulePriority.MEDIUM_HIGH -> HighlightDisplayLevels.CRITICAL RulePriority.MEDIUM -> HighlightDisplayLevels.MAJOR RulePriority.MEDIUM_LOW -> HighlightDisplayLevels.WARNING else -> HighlightDisplayLevels.WEAK_WARNING } } fun getRuleMessage(ruleName: String): String { return ruleMessages[ruleName]!! } private fun parseStaticDescription(rule: Rule): String { val writer = StringWriter() try { val map = Maps.newHashMap() map["message"] = StringUtils.trimToEmpty(rule.message) map["description"] = StringUtils.trimToEmpty(rule.description) val examples = rule.examples.map { it?.trim { c -> c == '\n' } } map["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[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.LocalQuickFix import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance import com.intellij.psi.* /** * @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, result: JavaResolveResult ): LocalQuickFix { val accessStaticViaInstanceFix : AccessStaticViaInstanceFix = object : AccessStaticViaInstanceFix(expr, result) { val fixKey = "com.alibaba.p3c.idea.quickfix.standalone.AliAccessStaticViaInstanceInspection" 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)) } } } return LocalQuickFix.from(accessStaticViaInstanceFix) as LocalQuickFix } 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:JavaResolveResult; try { result = expr.advancedResolve(false) } catch (e: Exception) { return } 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, 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 protected fun buildFix(vararg infos: Any): InspectionGadgetsFix? { val fix : LocalQuickFix try { fix = super.buildFix(*infos) ?: return null } catch (e: Exception){ 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 } this.registerMethodError(method, *arrayOf(true, true)) } 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 import java.util.Locale /** * @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).lowercase() } } 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 const val skipJdkPackageJava = "java." private const 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/lifecycle/P3cPluginLifecycle.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.lifecycle import com.intellij.ide.plugins.DynamicPluginListener import com.intellij.ide.plugins.IdeaPluginDescriptor import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.vfs.VirtualFileManager import com.alibaba.p3c.idea.component.AliProjectComponent import com.alibaba.p3c.idea.pmd.SourceCodeProcessor import com.alibaba.p3c.idea.inspection.AliPmdInspectionInvoker /** * Plugin lifecycle manager for dynamic plugin support. * Handles cleanup when plugin is unloaded without IDE restart. * * @author XenoAmess */ class P3cPluginLifecycle : DynamicPluginListener { companion object { const val PLUGIN_ID = "com.alibaba.p3c.xenoamess" } override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) { if (pluginDescriptor.pluginId.idString != PLUGIN_ID) { return } cleanupResources() } override fun pluginUnloaded(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) { // Additional cleanup if needed after plugin is unloaded } override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) { // Initialization when plugin is loaded dynamically } /** * Cleanup all resources to prevent memory leaks and allow proper plugin unloading. */ private fun cleanupResources() { // Clear all PMD caches SourceCodeProcessor.clearAllCaches() // Clear file violation caches AliPmdInspectionInvoker.clearAllFileViolationsCache() // Cleanup for each open project ProjectManager.getInstance().openProjects.forEach { project -> cleanupProject(project) } } /** * Cleanup resources for a specific project. */ private fun cleanupProject(project: Project) { try { // Remove VirtualFileListener for this project val projectComponent = project.getComponent(AliProjectComponent::class.java) projectComponent?.let { VirtualFileManager.getInstance().removeVirtualFileListener(it.getVirtualFileListener()) } } catch (ignored: Exception) { // Component might not be initialized or already disposed } } } ================================================ 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.compatible.inspection.Inspections 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 { Inspections.loadPatchConfigFile(psiFile.project) 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 net.sourceforge.pmd.lang.xpath.Initializer import java.io.IOException import java.io.InputStream import java.io.InputStreamReader 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) // make sure custom XPath functions are initialized Initializer.initialize() processSourceCodeWithoutCache(sourceCode, ruleSets, ctx) } @Throws(PMDException::class) private fun processSourceCodeWithoutCache(sourceCode: Reader, ruleSets: RuleSets, ctx: RuleContext) { 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) resolveQualifiedNames(rootNode, languageVersionHandler) 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() } /** * Clear all caches for dynamic plugin unload. */ fun clearAllCaches() { if (::onlyTheFlyCache.isInitialized) { onlyTheFlyCache.invalidateAll() } if (::userTriggerNodeCache.isInitialized) { 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 } override fun close() { // empty default implementation } } ================================================ 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.application.ApplicationManager 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 import java.lang.reflect.Method /** * * * @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 psiFile = psiIdentifier.containingFile commitDocumentIfNeeded(psiFile, project) val psiFacade = JavaPsiFacade.getInstance(project) val factory = psiFacade.elementFactory try { val application = ApplicationManager.getApplication() val isWriteThreadMethod: Method = application.javaClass.getDeclaredMethod("isWriteThread") val isWriteThread = isWriteThreadMethod.invoke(application) if (!(isWriteThread as Boolean)) { val anAction = ActionManager.getInstance().getAction("RenameElement") 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 } } anAction.actionPerformed(event) } } catch (e: Exception) { } finally { } // origin PsiIdentifier is unavailable psiFile.findElementAt(offset)?.replace(factory.createIdentifier(newIdentifier)) commitDocumentIfNeeded(psiFile, project) } 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.RegExUtils /** * * * @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 = RegExUtils.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 import java.util.Locale /** * * * @author caikang * @date 2017/02/28 */ object ConstantFieldShouldBeUpperCaseQuickFix : AliQuickFix { const 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).uppercase() } 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.LocalQuickFix 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: LocalQuickFix, 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].lowercaseChar() else charArray[0].uppercaseChar() 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 const 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) val WARNING = HighlightDisplayLevel(HighlightSeverities.WARNING, HighlightDisplayLevel.WEAK_WARNING.icon) val WEAK_WARNING = HighlightDisplayLevel(HighlightSeverities.WEAK_WARNING, 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) val WARNING: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.WARNING, CodeInsightColors.WEAK_WARNING_ATTRIBUTES) val WEAK_WARNING: HighlightInfoType = HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverities.WEAK_WARNING, 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 BLOCKER = HighlightSeverity("BLOCKER", 399) val CRITICAL = HighlightSeverity("CRITICAL", 398) val MAJOR = HighlightSeverity("MAJOR", 397) val WARNING = HighlightSeverity("WARNING", 396) val WEAK_WARNING = HighlightSeverity("WEAK_WARNING", 395) } ================================================ 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 { const val INTEGER_SIZE_OR_LENGTH_0 = 0 const val INTEGER_SIZE_OR_LENGTH_1 = 1 const val INTEGER_SIZE_OR_LENGTH_2 = 2 const val INDEX_0 = 0 const 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 { const val METHOD_NAME_EQUALS = "equals" const val METHOD_NAME_HASHCODE = "hashCode" const val METHOD_NAME_ADD = "add" const val METHOD_NAME_PUT = "put" const val CLASS_LITERAL = "class" const val INTERFACE_LITERAL = "interface" const 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.xenoamess.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 } val localQuickFix = quickFix(psiElement) ?: return null return manager.createProblemDescriptor( psiElement, endElement, desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, localQuickFix ) } 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? { val localQuickFix = quickFix(); if (localQuickFix == null) { return manager.createProblemDescriptor(psiFile, textRange, desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly) } return manager.createProblemDescriptor(psiFile, textRange, desc, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly, localQuickFix) } } ================================================ 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.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 = 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 fileList = java.util.ArrayList(virtualFiles.toList()) val analysisScope = AnalysisScope( project, fileList ) val tools = Inspections.aliInspections(project) { it.tool is AliBaseInspection } AliInspectionAction.createContext(tools, managerEx, null, false, analysisScope) .doInspections(analysisScope) } private fun filterOutGeneratedAndExcludedFiles(files: Collection, project: Project): List { return files.filter { file -> !file.isDirectory && file.isValid && !isFileExcluded(file, project) } } private fun isFileExcluded(file: VirtualFile, project: Project): Boolean { val changeListManager = com.intellij.openapi.vcs.changes.ChangeListManager.getInstance(project) return changeListManager.isIgnoredFile(file) } 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/activity/AliBaseApplicationStartupActivity.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.activity import com.intellij.openapi.startup.StartupActivity /** * * * @author caikang * @date 2017/05/11 */ interface AliBaseApplicationStartupActivity : StartupActivity ================================================ 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 org.apache.http.MethodNotSupportedException import java.awt.Component import java.net.UnknownHostException /** * * * @author caikang * @date 2017/05/08 */ object BalloonNotifications { const val displayId = "SmartFox Intellij IDEA Balloon Notification" val balloonGroup = buildNotificationGroup(displayId, NotificationDisplayType.BALLOON, true) const val stickyBalloonDisplayId = "SmartFox Intellij IDEA Notification" val stickyBalloonGroup = buildNotificationGroup(stickyBalloonDisplayId, NotificationDisplayType.STICKY_BALLOON, true) const 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 = buildNotificationGroup(displayId = BalloonNotifications.displayId, displayType = NotificationDisplayType.NONE, isLogByDefault = 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) } } fun buildNotificationGroup(displayId: String, displayType: NotificationDisplayType, isLogByDefault: Boolean): NotificationGroup { val notificationGroupClass = Class.forName("com.intellij.notification.NotificationGroup") notificationGroupClass.constructors.forEach { if (it.parameters.size == 3) { try { return it.newInstance(displayId, displayType, isLogByDefault) as NotificationGroup } catch (e: Exception) { System.err.println(e) } } } throw MethodNotSupportedException("cannot find a suitable constructor for NotificationGroup who accepts [String,NotificationDisplayType,Boolean]") } ================================================ 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.application.ApplicationManager import com.intellij.openapi.project.Project /** * * * @author caikang * @date 2017/06/19 */ fun Class.getService(): T { return ApplicationManager.getApplication().getService(this) } fun Class.getService(project: Project): T { return project.getService(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.PluginManagerCore import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.extensions.PluginId /** * @author caikang */ object PluginVersions { const val baseVersion141 = 141 const val baseVersion143 = 143 const val baseVersion145 = 145 const val baseVersion162 = 162 const val baseVersion163 = 163 const val baseVersion171 = 171 val pluginId: PluginId = (javaClass.classLoader as PluginClassLoader).pluginId val pluginDescriptor: IdeaPluginDescriptor = PluginManagerCore.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) 切换语言至中文 重启后生效]]> 编码规约扫描 关闭实时检测功能 打开实时检测功能 P3C 插件已禁用(无需重启 IDE) P3C 插件已启用(无需重启 IDE) 删除开头的 $ 或者 _ 添加/提取 @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 P3C plugin has been disabled (no IDE restart needed) P3C plugin has been enabled (no IDE restart needed) 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 'java' id("org.jetbrains.intellij.platform") version "2.16.0" id "com.github.ben-manes.versions" version "0.54.0" } repositories { mavenCentral() intellijPlatform { defaultRepositories() } } apply plugin: 'kotlin' apply plugin: 'idea' intellijPlatform { pluginConfiguration { name = plugin_name } buildSearchableOptions = false } dependencies { intellijPlatform { create("IC", idea_version) bundledPlugins(['com.intellij.java']) } implementation project(':p3c-common') } version plugin_version ================================================ FILE: idea-plugin/p3c-idea/src/main/resources/META-INF/plugin.xml ================================================ com.alibaba.p3c.xenoamess Alibaba Java Coding Guidelines(XenoAmess TPM) XenoAmess Alibaba Java Coding Guidelines plugin support.(XenoAmess TPM)


Third-party maintenance by XenoAmess.


This Third-party maintenance(TPM) here is forked from original alibaba/p3c


Follows Apache license described in license


Sources can be found https://github.com/XenoAmess/p3c


Releases can be found at https://plugins.jetbrains.com/plugin/14109-alibaba-java-coding-guidelines-xenoamess-tpm-


This TPM aims to help maintain alibaba/p3c, fix bugs, and add improvements, as the original developer is too busy to handle them.


This TPM is NOT created, or maintained, or controlled by any alibaba employee, in other words it is not a TPM, not an official branch.


TPM maintainer XenoAmess have no knowledge with eclipse plugin development.


TPM maintainer XenoAmess suggest you only create pr for module idea-plugin and p3c-pmd, unless you really have a strong reason.


]]>
2.3.0
  • (unstable) add config to enable/disable background analyzing
    • 2.2.4.3x
    • fix https://github.com/XenoAmess/p3c/issues/784
      2.2.4.2x
    • fix https://github.com/XenoAmess/p3c/issues/782 (thanks to zzqo)
      2.2.4.1x
    • fix https://github.com/XenoAmess/p3c/issues/778
      2.2.3.0x
    • Fix 2024.1 compatibility issue
    • Fix 2024.2 compatibility issue
    • upgrade gradle & building jdk & intellij plugin version
      2.2.2.0x
    • Fix 2023.2 compatibility issue
      2.2.1.0x
    • Fix @Override missing error
      2.2.0.0x
    • Fix idea 2023.1 2023.2 2023.3 compatibility issue
      2.1.1.6x-SNAPSHOT
    • Fix idea 2023.1-EAP compatibility issue
      2.1.1.5x
    • Migrate deprecated/removal idea function usages
      2.1.1.4x
    • Fix idea 2022.1 Ultimate compatibility issue
      2.1.1.3x
    • pmd upgrade to 6.44.0
    • fix bugs of SneakyThrowsWithoutExceptionTypeRule
    • add com.alibaba.p3c.pmd.config>package_blacklist config. see https://github.com/XenoAmess/p3c/issues/211
      2.1.1.2x
    • pmd upgrade to 6.41.0
    • now allow use json for configuration too (p3c_config.json). see https://github.com/XenoAmess/p3c/issues/144
    • add addition rules mechanism
    • add an addition rule to detect using lombok.SneakyThrow annotation while not specify the type of exception to throw.
    • update deprecated pmd grammars in ServiceOrDaoClassShouldEndWithImplRule : @Image to @SimpleName
    • update deprecated pmd grammars in TestClassShouldEndWithTestNamingRule : to suite codes changes in AbstractJUnitRule
      2.1.1.1x
    • sync some minor fix in 2.1.1
    • pmd upgrade to 6.37.0
    • update deprecated grammars in PojoMustOverrideToStringRule
    • update deprecated pmd grammars in AvoidNewDateGetTimeRule : @ArgumentCount to @Size
    • update deprecated pmd grammars in BooleanPropertyShouldNotStartWithIsRule : @Image to @Name
    • update deprecated pmd grammars in BooleanPropertyShouldNotStartWithIsRule : @Image to @SimpleName
    • update missing attribute language="java" for some ruleset xml.
    • update deprecated pmd grammars in AvoidComplexConditionRule : @Ternary to null(@Ternary is always true in new versions of PMD)
    • update deprecated pmd grammars in StringConcatRule : @Image to @Operator
    • update deprecated pmd grammars in PojoMustOverrideToStringRule : MethodDeclarator[@ParameterCount] to @Arity
    • update deprecated pmd grammars in PojoMustOverrideToStringRule : MethodDeclarator[@Image] to @Name
      2.1.1
    • Fix idea 2021.2 Ultimate compatibility issue
      2.1.0.7x
    • pmd upgrade to 6.36.0
    • upgrade other dependencies
    • fix https://github.com/alibaba/p3c/issues/832
    • fix https://github.com/XenoAmess/p3c/issues/118
    • fix https://github.com/alibaba/p3c/issues/757
      2.1.0.6x
    • pmd upgrade to 6.33.0
    • upgrade other dependencies
      2.1.0.5x
    • bugfix about LockShouldWithTryFinallyRule will throw NPE when node.type == null
      2.1.0.4x
    • pmd upgrade to 6.31.0
    • upgrade other dependencies
      2.1.0.3x
    • fix bugs about wrongly think new BigDecimal(Double.toString(0.0)) illegal. See: https://github.com/alibaba/p3c/issues/782
      2.1.0.2x
    • fix logics in rule LowerCamelCaseVariableNamingRule. Now we do not alarm in function with @Override
    • move package 'com.alibaba.p3c.pmd' to 'com.xenoamess.p3c.pmd'
      2.1.0.1x
    • pmd upgrade to 6.26.0
    • Kotlin upgrade to 1.4.0
      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.6x
    • fix code smells.
    • upgrade dependencies.
      2.0.2.5x
    • ignore AbstractMethodOrInterfaceMethodMustUseJavadocRule check for function who have @inheritdoc in javadoc.
    • fix some typos.
    • fix code smells.
    • upgrade dependencies.
      2.0.2.4x
    • add config mechanism to idea highlight.
    • upgrade x8l version.
    • fix code smells.
      2.0.2.3x
    • add config mechanism to both p3c-pmd and idea-plugin.
    • down priority EqualsHashCodeRule to 4.
    • EqualsHashCodeRule now only check non-Abstract class.
      2.0.2.2x
    • add SwitchExpressionRule to deal with switch expression, which is experimental in java 12 and 13, and finally public in java 14.Added a rule for checking SwitchExpression MUST have a default branch.
    • I'm new to pmd and XPATH thing, so if there be bug/false positive in the new added rule, please e-mail me or open issue on github.
      2.0.2.1x
    • learn from 2.0.2
    • update version of pmd from 6.18.0 to 6.22.0.
    • update version of kotlin from 1.3.71 to 1.3.72.
    • Bugfix about output some excess infos when analyze before commit. Thanks to geasskid@163.com 's bug report.
      2.0.2
    • Fix idea 2020.1 Ultimate compatibility issue
        2.0.1.4x
      • add a Exception catch for some code to make sure will not corrupt when idea lib adds some more functions in future.
      • update version of pmd from 6.17.0 to 6.18.0.
      • code changes due to pmd upgrade.
        2.0.1.3x
      • move groupID from com.alibaba.p3c to com.xenoamess.p3c, as I have no right to release an alibaba project in maven central.
      • bugfix for gitbook; add markdown guidelines.
      • remake pom for p3c-pmd.
      • reformat some kotlin codes
      • add travis-ci
      • add sonarcloud
        2.0.1.2x
      • reviews all pending prs before 2020/04/11 , and merged/improved some of them.
      • merged pr: special camel rule for MybatisGenerator's DOMapper classes.
      • merged pr: fix for check of BeanUtils' copyProperties, and add more tests for it.
      • merged pr: forbid Object Varargs, and change its priority from 1 to 4.
      • merged pr: forbid Object Varargs, and change its priority from 1 to 4.
      • upgrade some dependencies.
      • remake pom.xml in p3c-pmd.
      • fix some javadoc in p3c-pmd.
      • fix code smells.
      • fix some wrongly formatted xmls, including this plugin.xml.
      • add WARNING and WEAK_WARNING.
        2.0.1.1x
      • Fix idea 2020.1 compatibility issue
      • fix javadoc in p3c-cmd
      • fix clinic dependency in p3c-cmd
      • upgrade gradle wrapper
      • upgrade idea-plugin version
      • upgrade some dependencies
        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
      ]]> 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/p3c-idea/src/main/resources/META-INF/pluginWithJava.xml ================================================ com.alibaba.p3c.idea.component.AliProjectComponent ================================================ FILE: idea-plugin/settings.gradle ================================================ plugins { id "com.gradle.develocity" version "4.4.1" } develocity { buildScan { termsOfUseUrl = "https://gradle.com/terms-of-service" termsOfUseAgree = "yes" } } 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: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then export JAVA_HOME="`/usr/libexec/java_home`" else export JAVA_HOME="/Library/Java/Home" fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home 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 saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then 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 else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found .mvn/wrapper/maven-wrapper.jar" fi else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi if [ -n "$MVNW_REPOURL" ]; then jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" else jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" fi while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" if [ "$MVNW_VERBOSE" = true ]; then echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" if $cygwin; then wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` fi if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then wget "$jarUrl" -O "$wrapperJarPath" else wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" fi elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then curl -o "$wrapperJarPath" "$jarUrl" -f else curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f fi else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then javaClass=`cygpath --path --windows "$javaClass"` fi if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then echo " - Compiling MavenWrapperDownloader.java ..." fi # Compiling the Java class ("$JAVA_HOME/bin/javac" "$javaClass") fi if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then # Running the downloader if [ "$MVNW_VERBOSE" = true ]; then echo " - Running MavenWrapperDownloader.java ..." fi ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") fi fi fi fi ########################################################################################## # End of extension ########################################################################################## export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} if [ "$MVNW_VERBOSE" = true ]; then echo $MAVEN_PROJECTBASEDIR fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( if "%MVNW_VERBOSE%" == "true" ( echo Found %WRAPPER_JAR% ) ) else ( if not "%MVNW_REPOURL%" == "" ( SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %DOWNLOAD_URL% ) powershell -Command "&{"^ "$webclient = new-object System.Net.WebClient;"^ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% ) ) @REM End of extension @REM Provide a "standardized" way to retrieve the CLI args that will @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: p3c-formatter/eclipse-codestyle.xml ================================================ ================================================ FILE: p3c-formatter/eclipse-codetemplate.xml ================================================