Skip to content

Commit 8aa28bb

Browse files
author
Vasilii Burlacu
committed
added the duplicated plugin name inspection
1 parent 60e9ba5 commit 8aa28bb

File tree

5 files changed

+472
-0
lines changed

5 files changed

+472
-0
lines changed

resources/META-INF/plugin.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696

9797
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.EventObserverIndex" />
9898
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.EventNameIndex" />
99+
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.PluginNameIndex" />
99100
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.VirtualTypeIndex" />
100101
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.PluginIndex" />
101102
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.BlockNameIndex" />
@@ -134,6 +135,14 @@
134135
level="WARNING"
135136
implementationClass="com.magento.idea.magento2plugin.inspections.xml.ObserverDeclarationInspection"/>
136137

138+
<localInspection language="XML" groupPath="XML"
139+
shortName="PluginDeclarationInspection"
140+
displayName="Duplicated Plugin Usage in di XML"
141+
groupName="Magento 2"
142+
enabledByDefault="true"
143+
level="WARNING"
144+
implementationClass="com.magento.idea.magento2plugin.inspections.xml.PluginDeclarationInspection"/>
145+
137146
<localInspection language="XML" groupPath="XML"
138147
shortName="CacheableFalseInDefaultLayoutInspection"
139148
displayName="Inspection for disabled cache site-wide"

src/com/magento/idea/magento2plugin/indexes/IndexManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.intellij.util.indexing.FileBasedIndexImpl;
88
import com.intellij.util.indexing.ID;
99
import com.magento.idea.magento2plugin.stubs.indexes.*;
10+
import com.magento.idea.magento2plugin.stubs.indexes.PluginIndex;
1011
import com.magento.idea.magento2plugin.stubs.indexes.js.MagentoLibJsIndex;
1112
import com.magento.idea.magento2plugin.stubs.indexes.js.RequireJsIndex;
1213
import com.magento.idea.magento2plugin.stubs.indexes.graphql.GraphQlResolverIndex;
@@ -23,6 +24,7 @@ public static void manualReindex() {
2324
ModulePackageIndex.KEY,
2425
// xml|di configuration
2526
PluginIndex.KEY,
27+
PluginNameIndex.KEY,
2628
VirtualTypeIndex.KEY,
2729
// layouts
2830
BlockNameIndex.KEY,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
package com.magento.idea.magento2plugin.indexes;
6+
7+
import com.intellij.openapi.project.Project;
8+
import com.intellij.openapi.vfs.VirtualFile;
9+
import com.intellij.psi.PsiElement;
10+
import com.intellij.psi.PsiManager;
11+
import com.intellij.psi.search.GlobalSearchScope;
12+
import com.intellij.psi.xml.XmlAttributeValue;
13+
import com.intellij.psi.xml.XmlFile;
14+
import com.intellij.util.indexing.FileBasedIndex;
15+
import com.magento.idea.magento2plugin.xml.XmlPsiTreeUtil;
16+
17+
import java.util.ArrayList;
18+
import java.util.Collection;
19+
20+
public class PluginIndex {
21+
22+
private static PluginIndex INSTANCE;
23+
24+
private Project project;
25+
26+
private PluginIndex() {
27+
}
28+
29+
public static PluginIndex getInstance(final Project project) {
30+
if (null == INSTANCE) {
31+
INSTANCE = new PluginIndex();
32+
}
33+
INSTANCE.project = project;
34+
35+
return INSTANCE;
36+
}
37+
38+
public Collection<PsiElement> getPluginElements(final String name, final GlobalSearchScope scope) {
39+
Collection<PsiElement> result = new ArrayList<>();
40+
41+
Collection<VirtualFile> virtualFiles =
42+
FileBasedIndex.getInstance().getContainingFiles(
43+
com.magento.idea.magento2plugin.stubs.indexes.PluginIndex.KEY,
44+
name,
45+
scope
46+
);
47+
48+
for (VirtualFile virtualFile : virtualFiles) {
49+
XmlFile xmlFile = (XmlFile) PsiManager.getInstance(project).findFile(virtualFile);
50+
Collection<XmlAttributeValue> valueElements = XmlPsiTreeUtil
51+
.findAttributeValueElements(xmlFile, "type", "name", name);
52+
result.addAll(valueElements);
53+
}
54+
return result;
55+
}
56+
}
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2plugin.inspections.xml;
7+
8+
import com.intellij.codeInspection.ProblemHighlightType;
9+
import com.intellij.codeInspection.ProblemsHolder;
10+
import com.intellij.ide.highlighter.XmlFileType;
11+
import com.intellij.openapi.vfs.VfsUtil;
12+
import com.intellij.openapi.vfs.VirtualFile;
13+
import com.intellij.psi.*;
14+
import com.intellij.psi.search.GlobalSearchScope;
15+
import com.intellij.psi.util.PsiTreeUtil;
16+
import com.intellij.psi.xml.XmlAttribute;
17+
import com.intellij.psi.xml.XmlAttributeValue;
18+
import com.intellij.psi.xml.XmlDocument;
19+
import com.intellij.psi.xml.XmlTag;
20+
import com.jetbrains.php.lang.inspections.PhpInspection;
21+
import com.magento.idea.magento2plugin.indexes.PluginIndex;
22+
import com.magento.idea.magento2plugin.magento.files.ModuleXml;
23+
import com.magento.idea.magento2plugin.magento.packages.Package;
24+
import org.jetbrains.annotations.NotNull;
25+
import org.jetbrains.annotations.Nullable;
26+
27+
import java.net.MalformedURLException;
28+
import java.net.URL;
29+
import java.util.*;
30+
31+
public class PluginDeclarationInspection extends PhpInspection {
32+
33+
@NotNull
34+
@Override
35+
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean b) {
36+
return new XmlElementVisitor() {
37+
private final String moduleXmlFileName = ModuleXml.getInstance().getFileName();
38+
private static final String pluginsXmlFileName = "di.xml";
39+
private static final String duplicatedObserverNameSameFileProblemDescription = "The plugin name already used in this file. For more details see Inspection Description.";
40+
private static final String duplicatedObserverNameProblemDescription =
41+
"The plugin name \"%s\" for targeted \"%s\" class is already used in the module \"%s\" (%s scope). For more details see Inspection Description.";
42+
private HashMap<String, VirtualFile> loadedFileHash = new HashMap<>();
43+
private final ProblemHighlightType errorSeverity = ProblemHighlightType.WARNING;
44+
45+
@Override
46+
public void visitFile(PsiFile file) {
47+
if (!file.getName().equals(pluginsXmlFileName)) {
48+
return;
49+
}
50+
51+
XmlTag[] xmlTags = getFileXmlTags(file);
52+
PluginIndex pluginIndex = PluginIndex.getInstance(file.getProject());
53+
54+
if (xmlTags == null) {
55+
return;
56+
}
57+
58+
HashMap<String, XmlTag> targetObserversHash = new HashMap<>();
59+
60+
for (XmlTag pluginXmlTag: xmlTags) {
61+
HashMap<String, XmlTag> pluginProblems = new HashMap<>();
62+
if (!pluginXmlTag.getName().equals("type")) {
63+
continue;
64+
}
65+
66+
XmlAttribute pluginNameAttribute = pluginXmlTag.getAttribute("name");
67+
68+
String pluginNameAttributeValue = pluginNameAttribute.getValue();
69+
if (pluginNameAttributeValue == null) {
70+
continue;
71+
}
72+
73+
List<XmlTag> targetObservers = fetchObserverTagsFromPluginTag(pluginXmlTag);
74+
75+
for (XmlTag pluginTypeXmlTag: targetObservers) {
76+
XmlAttribute pluginTypeNameAttribute = pluginTypeXmlTag.getAttribute("name");
77+
XmlAttribute pluginTypeDisabledAttribute = pluginTypeXmlTag.getAttribute("disabled");
78+
79+
if (pluginTypeNameAttribute == null || (pluginTypeDisabledAttribute != null && pluginTypeDisabledAttribute.getValue().equals("true"))) {
80+
continue;
81+
}
82+
83+
String pluginTypeName = pluginTypeNameAttribute.getValue();
84+
String pluginTypeKey = pluginNameAttributeValue.concat("_").concat(pluginTypeName);
85+
if (targetObserversHash.containsKey(pluginTypeKey)) {
86+
problemsHolder.registerProblem(
87+
pluginTypeNameAttribute.getValueElement(),
88+
duplicatedObserverNameSameFileProblemDescription,
89+
errorSeverity
90+
);
91+
}
92+
targetObserversHash.put(pluginTypeKey, pluginTypeXmlTag);
93+
94+
List<HashMap<String, String>> modulesWithSameObserverName = fetchModuleNamesWhereSamePluginNameUsed(pluginNameAttributeValue, pluginTypeName, pluginIndex, file);
95+
for (HashMap<String, String> moduleEntry: modulesWithSameObserverName) {
96+
Map.Entry<String, String> module = moduleEntry.entrySet().iterator().next();
97+
String moduleName = module.getKey();
98+
String scope = module.getValue();
99+
String problemKey = pluginTypeKey.concat("_").concat(moduleName).concat("_").concat(scope);
100+
if (!pluginProblems.containsKey(problemKey)){
101+
problemsHolder.registerProblem(
102+
pluginTypeNameAttribute.getValueElement(),
103+
String.format(
104+
duplicatedObserverNameProblemDescription,
105+
pluginTypeName,
106+
pluginNameAttributeValue,
107+
moduleName,
108+
scope
109+
),
110+
errorSeverity
111+
);
112+
pluginProblems.put(problemKey, pluginTypeXmlTag);
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
private List<HashMap<String, String>> fetchModuleNamesWhereSamePluginNameUsed(String pluginNameAttributeValue, String pluginTypeName, PluginIndex pluginIndex, PsiFile file) {
120+
List<HashMap<String, String>> modulesName = new ArrayList<>();
121+
String currentFileDirectory = file.getContainingDirectory().toString();
122+
String currentFileFullPath = currentFileDirectory.concat("/").concat(file.getName());
123+
124+
Collection<PsiElement> indexedPlugins = pluginIndex.getPluginElements(pluginNameAttributeValue, GlobalSearchScope.getScopeRestrictedByFileTypes(
125+
GlobalSearchScope.allScope(file.getProject()),
126+
XmlFileType.INSTANCE
127+
));
128+
129+
for (PsiElement indexedPlugin: indexedPlugins) {
130+
PsiFile indexedAttributeParent = PsiTreeUtil.getTopmostParentOfType(indexedPlugin, PsiFile.class);
131+
if (indexedAttributeParent == null) {
132+
continue;
133+
}
134+
135+
String indexedPluginAttributeValue = ((XmlAttributeValue) indexedPlugin).getValue();
136+
if (!indexedPluginAttributeValue.equals(pluginNameAttributeValue)) {
137+
continue;
138+
}
139+
140+
String indexedFileDirectory = indexedAttributeParent.getContainingDirectory().toString();
141+
String indexedFileFullPath = indexedFileDirectory.concat("/").concat(indexedAttributeParent.getName());
142+
if (indexedFileFullPath.equals(currentFileFullPath)) {
143+
continue;
144+
}
145+
146+
String scope = getAreaFromFileDirectory(indexedAttributeParent);
147+
148+
List<XmlTag> indexObserversTags = fetchObserverTagsFromPluginTag((XmlTag) indexedPlugin.getParent().getParent());
149+
for (XmlTag indexObserversTag: indexObserversTags) {
150+
XmlAttribute indexedObserverNameAttribute = indexObserversTag.getAttribute("name");
151+
if (indexedObserverNameAttribute == null) {
152+
continue;
153+
}
154+
if (!pluginTypeName.equals(indexedObserverNameAttribute.getValue())){
155+
continue;
156+
}
157+
addModuleNameWhereSamePluginUsed(modulesName, indexedAttributeParent, scope);
158+
}
159+
}
160+
161+
return modulesName;
162+
}
163+
164+
private List<XmlTag> fetchObserverTagsFromPluginTag(XmlTag pluginXmlTag) {
165+
List<XmlTag> result = new ArrayList<>();
166+
XmlTag[] pluginTypeXmlTags = PsiTreeUtil.getChildrenOfType(pluginXmlTag, XmlTag.class);
167+
if (pluginTypeXmlTags == null) {
168+
return result;
169+
}
170+
171+
for (XmlTag pluginTypeXmlTag: pluginTypeXmlTags) {
172+
if (!pluginTypeXmlTag.getName().equals("plugin")) {
173+
continue;
174+
}
175+
176+
result.add(pluginTypeXmlTag);
177+
}
178+
179+
return result;
180+
}
181+
182+
private void addModuleNameWhereSamePluginUsed(List<HashMap<String, String>> modulesName, PsiFile indexedFile, String scope) {
183+
XmlTag moduleDeclarationTag = getModuleDeclarationTagByConfigFile(indexedFile);
184+
if (moduleDeclarationTag == null) return;
185+
186+
if (!moduleDeclarationTag.getName().equals("module")) {
187+
return;
188+
}
189+
XmlAttribute moduleNameAttribute = moduleDeclarationTag.getAttribute("name");
190+
if (moduleNameAttribute == null) {
191+
return;
192+
}
193+
194+
HashMap<String, String> moduleEntry = new HashMap<>();
195+
196+
moduleEntry.put(moduleNameAttribute.getValue(), scope);
197+
modulesName.add(moduleEntry);
198+
}
199+
200+
@Nullable
201+
private XmlTag getModuleDeclarationTagByConfigFile(PsiFile file) {
202+
String fileDirectory = file.getContainingDirectory().toString();
203+
String fileArea = file.getContainingDirectory().getName();
204+
String moduleXmlFilePath = getModuleXmlFilePathByConfigFileDirectory(fileDirectory, fileArea);
205+
206+
VirtualFile virtualFile = getFileByPath(moduleXmlFilePath);
207+
if (virtualFile == null) return null;
208+
209+
PsiFile moduleDeclarationFile = PsiManager.getInstance(file.getProject()).findFile(virtualFile);
210+
XmlTag[] moduleDeclarationTags = getFileXmlTags(moduleDeclarationFile);
211+
if (moduleDeclarationTags == null) {
212+
return null;
213+
}
214+
return moduleDeclarationTags[0];
215+
}
216+
217+
@Nullable
218+
private VirtualFile getFileByPath(String moduleXmlFilePath) {
219+
if (loadedFileHash.containsKey(moduleXmlFilePath)) {
220+
return loadedFileHash.get(moduleXmlFilePath);
221+
}
222+
VirtualFile virtualFile;
223+
try {
224+
virtualFile = VfsUtil.findFileByURL(new URL(moduleXmlFilePath));
225+
} catch (MalformedURLException e) {
226+
return null;
227+
}
228+
if (virtualFile == null) {
229+
return null;
230+
}
231+
loadedFileHash.put(moduleXmlFilePath, virtualFile);
232+
return virtualFile;
233+
}
234+
235+
private String getModuleXmlFilePathByConfigFileDirectory(String fileDirectory, String fileArea) {
236+
String moduleXmlFile = fileDirectory.replace(fileArea, "").concat(moduleXmlFileName);
237+
if (fileDirectory.endsWith("etc")) {
238+
moduleXmlFile = fileDirectory.concat("/").concat(moduleXmlFileName);
239+
}
240+
return moduleXmlFile.replace("PsiDirectory:", "file:");
241+
}
242+
243+
@Nullable
244+
private XmlTag[] getFileXmlTags(PsiFile file) {
245+
XmlDocument xmlDocument = PsiTreeUtil.getChildOfType(file, XmlDocument.class);
246+
XmlTag xmlRootTag = PsiTreeUtil.getChildOfType(xmlDocument, XmlTag.class);
247+
return PsiTreeUtil.getChildrenOfType(xmlRootTag, XmlTag.class);
248+
}
249+
250+
private String getAreaFromFileDirectory(@NotNull PsiFile file) {
251+
if (file.getParent() == null) {
252+
return "";
253+
}
254+
255+
String areaFromFileDirectory = file.getParent().getName();
256+
257+
if (areaFromFileDirectory.equals(Package.MODULE_BASE_AREA_DIR)) {
258+
return Package.Areas.base.toString();
259+
}
260+
261+
for (Package.Areas area: Package.Areas.values()) {
262+
if (area.toString().equals(areaFromFileDirectory)) {
263+
return area.toString();
264+
}
265+
}
266+
267+
return "";
268+
}
269+
};
270+
}
271+
}

0 commit comments

Comments
 (0)