From 2e10f3348751be50ddb352b02bb2b595d8efd614 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 9 Oct 2013 11:51:52 +0200 Subject: [PATCH] IDEA-99241 (Report package naming convention violations in the editor) --- .../codeInspection/GlobalInspectionTool.java | 26 +++++- .../ex/GlobalInspectionToolWrapper.java | 11 +++ .../daemon/impl/LocalInspectionsPass.java | 4 + .../src/com/siyeh/ig/BaseInspection.java | 6 +- .../siyeh/ig/BaseSharedLocalInspection.java | 45 +++++++++++ .../PackageNamingConventionInspection.java | 81 ++++++++++++++++++- .../PackageNamingConvention.html | 4 +- .../com/siyeh/ig/LightInspectionTestCase.java | 9 ++- ...PackageNamingConventionInspectionTest.java | 40 +++++++++ 9 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/PackageNamingConventionInspectionTest.java diff --git a/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java b/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java index 5e72c932b71c..077afec102b8 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,10 @@ import org.jetbrains.annotations.Nullable; * complete graph of references between classes, methods and other elements in the scope * selected for the analysis. * + * Global inspections can use a shared local inspection tool for highlighting the cases + * that do not need global analysis in the editor by implementing {@link #getSharedLocalInspectionTool()} + * The shared local inspection tools shares settings and documentation with the global inspection tool. + * * @author anna * @see LocalInspectionTool * @since 6.0 @@ -193,9 +197,25 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry { return null; } - // In some cases we can do highlighting in annotator or high. visitor based on global inspection + /** + * In some cases we can do highlighting in annotator or high. visitor based on global inspection or use a shared local inspection tool + */ public boolean worksInBatchModeOnly() { - return true; + return getSharedLocalInspectionTool() != null; + } + + /** + * Returns the local inspection tool used for highlighting in the editor. Meant for global inspections which have a local component. + * The local inspection tool is not required to report on the exact same problems, and naturally can't use global analysis. The local + * inspection tool is not used in batch mode. + * + * For example a global inspection that reports a package could have a local inspection tool which highlights + * the package statement in a file. + * @return + */ + @Nullable + public LocalInspectionTool getSharedLocalInspectionTool() { + return null; } public void initialize(@NotNull GlobalInspectionContext context) { diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionToolWrapper.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionToolWrapper.java index 5fb8e9442308..8f6e87c3c495 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionToolWrapper.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionToolWrapper.java @@ -18,10 +18,12 @@ package com.intellij.codeInspection.ex; import com.intellij.codeInspection.GlobalInspectionContext; import com.intellij.codeInspection.GlobalInspectionTool; import com.intellij.codeInspection.InspectionEP; +import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.reference.RefGraphAnnotator; import com.intellij.codeInspection.reference.RefManagerImpl; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * User: anna @@ -72,4 +74,13 @@ public class GlobalInspectionToolWrapper extends InspectionToolWrapper aClass = getClass(); final String name = aClass.getName(); @@ -150,7 +150,7 @@ public abstract class BaseInspection extends BaseJavaBatchLocalInspectionTool { for (List out : outs) { out.clear(); } - int iMax = strings.size(); + final int iMax = strings.size(); for (int i = 0; i < iMax; i += outs.length) { for (int j = 0; j < outs.length; j++) { final List out = outs[j]; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java new file mode 100644 index 000000000000..561ed26b4c97 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseSharedLocalInspection.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig; + +import com.intellij.codeInspection.GlobalInspectionTool; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Bas Leijdekkers + */ +public abstract class BaseSharedLocalInspection extends BaseInspection { + + protected final T mySettingsDelegate; + + public BaseSharedLocalInspection(T settingsDelegate) { + mySettingsDelegate = settingsDelegate; + } + + @NotNull + @Override + public final String getShortName() { + return mySettingsDelegate.getShortName(); + } + + @Nls + @NotNull + @Override + public final String getDisplayName() { + return mySettingsDelegate.getDisplayName(); + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java index 6c8e91c8b45f..7bad29330828 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/PackageNamingConventionInspection.java @@ -19,12 +19,18 @@ import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.CommonProblemDescriptor; import com.intellij.codeInspection.GlobalInspectionContext; import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefPackage; import com.intellij.codeInspection.ui.ConventionOptionsPanel; import com.intellij.openapi.util.InvalidDataException; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.PsiPackageStatement; +import com.siyeh.HardcodedMethodConstants; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseGlobalInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.BaseSharedLocalInspection; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -91,8 +97,7 @@ public class PackageNamingConventionInspection extends BaseGlobalInspection { else { final String errorString = InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.regex.mismatch", name, m_regex); - return new CommonProblemDescriptor[]{ - inspectionManager.createProblemDescriptor(errorString)}; + return new CommonProblemDescriptor[]{inspectionManager.createProblemDescriptor(errorString)}; } } @@ -106,4 +111,76 @@ public class PackageNamingConventionInspection extends BaseGlobalInspection { public JComponent createOptionsPanel() { return new ConventionOptionsPanel(this, "m_minLength", "m_maxLength", "m_regex", "m_regexPattern"); } + + boolean isValid(String name) { + final int length = name.length(); + if (length < m_minLength) { + return false; + } + if (m_maxLength > 0 && length > m_maxLength) { + return false; + } + if (HardcodedMethodConstants.SERIAL_VERSION_UID.equals(name)) { + return true; + } + final Matcher matcher = m_regexPattern.matcher(name); + return matcher.matches(); + } + + @Nullable + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalPackageNamingConventionInspection(this); + } + + private static class LocalPackageNamingConventionInspection extends BaseSharedLocalInspection { + + public LocalPackageNamingConventionInspection(PackageNamingConventionInspection inspection) { + super(inspection); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final String name = (String)infos[0]; + if (name.length() < mySettingsDelegate.m_minLength) { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.short", name); + } + else if (name.length() > mySettingsDelegate.m_maxLength) { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.long", name); + } + else { + return InspectionGadgetsBundle.message("package.naming.convention.problem.descriptor.regex.mismatch", + name, mySettingsDelegate.m_regex); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + + @Override + public void visitPackageStatement(PsiPackageStatement statement) { + final PsiJavaCodeReferenceElement reference = statement.getPackageReference(); + if (reference == null) { + return; + } + final String text = reference.getText(); + int start = 0; + int index = text.indexOf('.', start); + while (index > 0) { + final String name = text.substring(start, index); + if (!mySettingsDelegate.isValid(name)) { + registerErrorAtOffset(reference, start, index - start, name); + } + start = index + 1; + index = text.indexOf('.', start); + } + final String lastName = text.substring(start); + if (!mySettingsDelegate.isValid(lastName)) { + registerErrorAtOffset(reference, start, lastName.length(), lastName); + } + } + }; + } + } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageNamingConvention.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageNamingConvention.html index 24f590bcacba..db67839cf44b 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PackageNamingConvention.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PackageNamingConvention.html @@ -1,10 +1,10 @@ This global inspection reports packages whose names are either too short, too long, or do not follow -the specified regular expression pattern. Since this inspection requires global code analysis, it is only available in batch inspection mode. +the specified regular expression pattern.

-Use the fields below to specify minimum length, maximum length and regular expression expected for method parameter names +Use the fields below to specify minimum length, maximum length and regular expression expected for package names (Regular expressions are in standard java.util.regex format).

New in 11 diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/LightInspectionTestCase.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/LightInspectionTestCase.java index e045e8a39658..20ce1a996db2 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/LightInspectionTestCase.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/LightInspectionTestCase.java @@ -15,8 +15,9 @@ */ package com.siyeh.ig; -import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.InspectionProfileEntry; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import com.intellij.util.ArrayUtil; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -36,11 +37,11 @@ public abstract class LightInspectionTestCase extends LightCodeInsightFixtureTes myFixture.enableInspections(getInspection()); } - protected abstract LocalInspectionTool getInspection(); + protected abstract InspectionProfileEntry getInspection(); @NonNls protected String[] getEnvironmentClasses() { - return new String[]{}; + return ArrayUtil.EMPTY_STRING_ARRAY; } protected void addEnvironmentClass(@Language("JAVA") @NotNull @NonNls String classText) { @@ -82,7 +83,7 @@ public abstract class LightInspectionTestCase extends LightCodeInsightFixtureTes @Override protected String getBasePath() { - final Class inspectionClass = getInspection().getClass(); + final Class inspectionClass = getInspection().getClass(); final String className = inspectionClass.getName(); final String[] words = className.split("\\."); @NonNls final StringBuilder basePath = new StringBuilder("/plugins/InspectionGadgets/test/"); diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/PackageNamingConventionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/PackageNamingConventionInspectionTest.java new file mode 100644 index 000000000000..8e33fea325cd --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/naming/PackageNamingConventionInspectionTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * (c) 2013 Desert Island BV + * created: 09 10 2013 + */ +package com.siyeh.ig.naming; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; + +/** + * @author Bas Leijdekkers + */ +public class PackageNamingConventionInspectionTest extends LightInspectionTestCase { + + public void testSharedLocalInspection() { + doTest("package /*Package name 'a' is too short*/a/**/./*Package name 'bbbbbbbbbbbbbbbbbbbb' is too long*/bbbbbbbbbbbbbbbbbbbb/**/;" + + "class X {}"); + } + + @Override + protected InspectionProfileEntry getInspection() { + return new PackageNamingConventionInspection(); + } +}