Skip to content

Commit 88dfb3d

Browse files
committed
Develop preference xml inspections
1 parent a622f2a commit 88dfb3d

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

resources/META-INF/plugin.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@
204204
enabledByDefault="true" level="ERROR"
205205
implementationClass="com.magento.idea.magento2plugin.inspections.xml.AclResourceXmlInspection"/>
206206

207+
<localInspection language="XML" groupPath="XML"
208+
shortName="PreferenceXmlInspections"
209+
displayName="Inspection the existence of PHP classes for attributes in the preference"
210+
groupName="Magento 2"
211+
enabledByDefault="true"
212+
level="WARNING"
213+
implementationClass="com.magento.idea.magento2plugin.inspections.xml.PreferenceDeclarationInspection"/>
214+
207215
<internalFileTemplate name="Magento Composer JSON"/>
208216
<internalFileTemplate name="Magento Registration PHP"/>
209217
<internalFileTemplate name="Magento Module XML"/>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html>
2+
<body>
3+
<p>Write your description here.
4+
Start the description with a verb in 3rd person singular, like reports, detects, highlights.
5+
In the first sentence, briefly explain what exactly the inspection helps you detect.
6+
Make sure the sentence is not very long and complicated.
7+
The first sentence must be in a dedicated paragraph separated from the rest of the text. This will make the
8+
description easier to read.
9+
Make sure the description doesn’t just repeat the inspection title.
10+
</p>
11+
<!-- tooltip end -->
12+
<p>Text after this comment will only be shown in the settings of the inspection.</p>
13+
</body>
14+
</html>

resources/magento2/inspection.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ inspection.moduleDeclaration.warning.wrongModuleName=Provided module name "{0}"
2222
inspection.moduleDeclaration.fix=Fix module name
2323
inspection.aclResource.error.missingAttribute=Attribute "{0}" is required
2424
inspection.aclResource.error.idAttributeCanNotBeEmpty=Attribute value "{0}" can not be empty
25+
inspection.preference.error.notExist=Class does not exist
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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.psi.PsiElementVisitor;
11+
import com.intellij.psi.PsiFile;
12+
import com.intellij.psi.XmlElementVisitor;
13+
import com.intellij.psi.util.PsiTreeUtil;
14+
import com.intellij.psi.xml.XmlAttribute;
15+
import com.intellij.psi.xml.XmlDocument;
16+
import com.intellij.psi.xml.XmlTag;
17+
import com.jetbrains.php.lang.inspections.PhpInspection;
18+
import com.jetbrains.php.lang.psi.elements.PhpClass;
19+
import com.magento.idea.magento2plugin.bundles.InspectionBundle;
20+
import com.magento.idea.magento2plugin.magento.files.ModuleDiXml;
21+
import com.magento.idea.magento2plugin.util.GetPhpClassByFQN;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
@SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NPathComplexity"})
26+
public class PreferenceDeclarationInspection extends PhpInspection {
27+
28+
@Override
29+
public @NotNull PsiElementVisitor buildVisitor(
30+
final @NotNull ProblemsHolder problemsHolder,
31+
final boolean isOnTheFly
32+
) {
33+
return new XmlElementVisitor() {
34+
private final InspectionBundle inspectionBundle = new InspectionBundle();
35+
36+
@Override
37+
public void visitFile(final @NotNull PsiFile file) {
38+
if (!file.getName().equals(ModuleDiXml.FILE_NAME)) {
39+
return;
40+
}
41+
42+
final XmlTag[] xmlTags = getFileXmlTags(file);
43+
44+
if (xmlTags == null) {
45+
return;
46+
}
47+
48+
for (final XmlTag preferenceXmlTag : xmlTags) {
49+
if (!preferenceXmlTag.getName().equals(ModuleDiXml.PREFERENCE_TAG_NAME)) {
50+
continue;
51+
}
52+
53+
final XmlAttribute preferenceForAttribute =
54+
preferenceXmlTag.getAttribute(ModuleDiXml.PREFERENCE_ATTR_FOR);
55+
final XmlAttribute preferenceTypeAttribute =
56+
preferenceXmlTag.getAttribute(ModuleDiXml.TYPE_ATTR);
57+
58+
if (preferenceForAttribute == null || preferenceTypeAttribute == null) {
59+
continue;
60+
}
61+
62+
final Boolean isPreferenceForClassExists =
63+
isXmlAttributeValueClassExists(preferenceForAttribute);
64+
65+
if (isPreferenceForClassExists != null && !isPreferenceForClassExists) {
66+
reportClassDoesntExistsProblem(preferenceForAttribute);
67+
}
68+
69+
final Boolean isPreferenceTypeClassExists =
70+
isXmlAttributeValueClassExists(preferenceTypeAttribute);
71+
72+
if (isPreferenceTypeClassExists != null && !isPreferenceTypeClassExists) {
73+
reportClassDoesntExistsProblem(preferenceTypeAttribute);
74+
}
75+
}
76+
}
77+
78+
/**
79+
* Output a warning in the xml class.
80+
*
81+
* @param xmlAttribute XmlAttribute
82+
*/
83+
private void reportClassDoesntExistsProblem(final @NotNull XmlAttribute xmlAttribute) {
84+
if (xmlAttribute.getValueElement() == null) {
85+
return;
86+
}
87+
problemsHolder.registerProblem(
88+
xmlAttribute.getValueElement(),
89+
inspectionBundle.message("inspection.preference.error.notExist"),
90+
ProblemHighlightType.WARNING
91+
);
92+
}
93+
94+
/**
95+
* Checks the existence of the php class in the specified directory.
96+
*
97+
* @param xmlAttribute XmlAttribute
98+
*
99+
* @return attributeValueClass
100+
*/
101+
private @Nullable Boolean isXmlAttributeValueClassExists(
102+
final @NotNull XmlAttribute xmlAttribute
103+
) {
104+
final String attributeValue = xmlAttribute.getValue();
105+
106+
if (attributeValue == null) {
107+
return null;
108+
}
109+
110+
final PhpClass attributeValueClass =
111+
GetPhpClassByFQN.getInstance(xmlAttribute.getProject())
112+
.execute(attributeValue);
113+
114+
return attributeValueClass != null;
115+
}
116+
117+
/**
118+
* Get all children xml tags for root xml tag of file.
119+
*
120+
* @param file PsiFile
121+
*
122+
* @return XmlTag[]
123+
*/
124+
private @Nullable XmlTag[] getFileXmlTags(final @NotNull PsiFile file) {
125+
final XmlDocument xmlDocument = PsiTreeUtil.getChildOfType(file, XmlDocument.class);
126+
final XmlTag xmlRootTag = PsiTreeUtil.getChildOfType(xmlDocument, XmlTag.class);
127+
128+
return PsiTreeUtil.getChildrenOfType(xmlRootTag, XmlTag.class);
129+
}
130+
};
131+
}
132+
}

0 commit comments

Comments
 (0)