Skip to content

Commit e03abae

Browse files
committed
refactoring code and adding tests
1 parent 6e24fa5 commit e03abae

File tree

8 files changed

+247
-76
lines changed

8 files changed

+247
-76
lines changed
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
<!--
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
-->
17
<html>
28
<body>
39
<p>
4-
This inspection checks the existence of the classes specified for the "for" and "type" attributes in the preference.
10+
Validates classes and interfaces for attributes "for" and the "type" for the "preference" tag
511
</p>
12+
<p>This inspection checks the "for" and "type" attribute of the &ltpreference/&gt; tag for:</p>
13+
<ul>
14+
<li>the correct path to the class or interface</li>
15+
<li>existence of attribute values</li>
16+
</ul>
617
</body>
718
</html>

src/com/magento/idea/magento2plugin/inspections/xml/PreferenceDeclarationInspection.java

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,21 @@
77

88
import com.intellij.codeInspection.ProblemHighlightType;
99
import com.intellij.codeInspection.ProblemsHolder;
10+
import com.intellij.codeInspection.XmlSuppressableInspectionTool;
11+
import com.intellij.psi.PsiElement;
1012
import com.intellij.psi.PsiElementVisitor;
1113
import com.intellij.psi.PsiFile;
1214
import com.intellij.psi.XmlElementVisitor;
13-
import com.intellij.psi.util.PsiTreeUtil;
1415
import com.intellij.psi.xml.XmlAttribute;
15-
import com.intellij.psi.xml.XmlDocument;
1616
import com.intellij.psi.xml.XmlTag;
17-
import com.jetbrains.php.lang.inspections.PhpInspection;
18-
import com.jetbrains.php.lang.psi.elements.PhpClass;
1917
import com.magento.idea.magento2plugin.bundles.InspectionBundle;
18+
import com.magento.idea.magento2plugin.inspections.validator.InspectionValidator;
19+
import com.magento.idea.magento2plugin.inspections.validator.NotEmptyValidator;
20+
import com.magento.idea.magento2plugin.inspections.validator.PhpClassExistenceValidator;
2021
import com.magento.idea.magento2plugin.magento.files.ModuleDiXml;
21-
import com.magento.idea.magento2plugin.util.GetPhpClassByFQN;
2222
import org.jetbrains.annotations.NotNull;
23-
import org.jetbrains.annotations.Nullable;
2423

25-
@SuppressWarnings({"PMD.ExcessiveMethodLength", "PMD.NPathComplexity"})
26-
public class PreferenceDeclarationInspection extends PhpInspection {
24+
public class PreferenceDeclarationInspection extends XmlSuppressableInspectionTool {
2725

2826
@Override
2927
public @NotNull PsiElementVisitor buildVisitor(
@@ -33,99 +31,103 @@ public class PreferenceDeclarationInspection extends PhpInspection {
3331
return new XmlElementVisitor() {
3432
private final InspectionBundle inspectionBundle = new InspectionBundle();
3533

34+
private final InspectionValidator phpClassExistenceValidator =
35+
new PhpClassExistenceValidator(problemsHolder.getProject());
36+
private final InspectionValidator notEmptyValidator = new NotEmptyValidator();
37+
3638
@Override
37-
public void visitFile(final @NotNull PsiFile file) {
38-
if (!file.getName().equals(ModuleDiXml.FILE_NAME)) {
39+
public void visitXmlTag(final XmlTag xmlTag) {
40+
final PsiFile file = xmlTag.getContainingFile();
41+
42+
if (!file.getName().equals(ModuleDiXml.FILE_NAME)
43+
|| !xmlTag.getName().equals(ModuleDiXml.PREFERENCE_TAG_NAME)) {
3944
return;
4045
}
4146

42-
final XmlTag[] xmlTags = getFileXmlTags(file);
47+
final XmlAttribute preferenceForAttribute =
48+
xmlTag.getAttribute(ModuleDiXml.PREFERENCE_ATTR_FOR);
49+
final XmlAttribute preferenceTypeAttribute =
50+
xmlTag.getAttribute(ModuleDiXml.TYPE_ATTR);
4351

44-
if (xmlTags == null) {
52+
if (preferenceForAttribute == null
53+
|| preferenceForAttribute.getValue() == null
54+
|| preferenceForAttribute.getValueElement() == null) {
4555
return;
4656
}
4757

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-
}
58+
if (!notEmptyValidator.validate(preferenceForAttribute.getValue())) {
59+
reportCouldNotBeEmpty(
60+
preferenceForAttribute.getValueElement(),
61+
preferenceForAttribute.getName()
62+
);
63+
}
6164

62-
final Boolean isPreferenceForClassExists =
63-
isXmlAttributeValueClassExists(preferenceForAttribute);
65+
if (preferenceTypeAttribute == null
66+
|| preferenceTypeAttribute.getValue() == null
67+
|| preferenceTypeAttribute.getValueElement() == null) {
68+
return;
69+
}
6470

65-
if (isPreferenceForClassExists != null && !isPreferenceForClassExists) {
66-
reportClassDoesntExistsProblem(preferenceForAttribute);
67-
}
71+
if (!notEmptyValidator.validate(preferenceTypeAttribute.getValue())) {
72+
reportCouldNotBeEmpty(
73+
preferenceTypeAttribute.getValueElement(),
74+
preferenceTypeAttribute.getName()
75+
);
76+
}
6877

69-
final Boolean isPreferenceTypeClassExists =
70-
isXmlAttributeValueClassExists(preferenceTypeAttribute);
78+
if (!phpClassExistenceValidator.validate(preferenceForAttribute.getValue())) {
79+
reportClassDoesNotExists(
80+
preferenceForAttribute.getValueElement(),
81+
preferenceForAttribute.getValue()
82+
);
83+
}
7184

72-
if (isPreferenceTypeClassExists != null && !isPreferenceTypeClassExists) {
73-
reportClassDoesntExistsProblem(preferenceTypeAttribute);
74-
}
85+
if (!phpClassExistenceValidator.validate(preferenceTypeAttribute.getValue())) {
86+
reportClassDoesNotExists(
87+
preferenceTypeAttribute.getValueElement(),
88+
preferenceTypeAttribute.getValue()
89+
);
7590
}
7691
}
7792

7893
/**
79-
* Output a warning in the xml class.
94+
* Report Attribute Value could not be empty.
8095
*
81-
* @param xmlAttribute XmlAttribute
96+
* @param psiElement PsiElement
97+
* @param messageParams Object...
8298
*/
83-
private void reportClassDoesntExistsProblem(final @NotNull XmlAttribute xmlAttribute) {
84-
if (xmlAttribute.getValueElement() == null) {
85-
return;
86-
}
99+
private void reportCouldNotBeEmpty(
100+
final @NotNull PsiElement psiElement,
101+
final Object... messageParams
102+
) {
87103
problemsHolder.registerProblem(
88-
xmlAttribute.getValueElement(),
89-
inspectionBundle.message("inspection.preference.error.notExist"),
90-
ProblemHighlightType.WARNING
104+
psiElement,
105+
inspectionBundle.message(
106+
"inspection.error.idAttributeCanNotBeEmpty",
107+
messageParams
108+
),
109+
ProblemHighlightType.ERROR
91110
);
92111
}
93112

94113
/**
95-
* Checks the existence of the php class in the specified directory.
96-
*
97-
* @param xmlAttribute XmlAttribute
114+
* Report class does not exists.
98115
*
99-
* @return attributeValueClass
116+
* @param psiElement PsiElement
117+
* @param messageParams Object...
100118
*/
101-
private @Nullable Boolean isXmlAttributeValueClassExists(
102-
final @NotNull XmlAttribute xmlAttribute
119+
private void reportClassDoesNotExists(
120+
final @NotNull PsiElement psiElement,
121+
final Object... messageParams
103122
) {
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);
123+
problemsHolder.registerProblem(
124+
psiElement,
125+
inspectionBundle.message(
126+
"inspection.warning.class.does.not.exist",
127+
messageParams
128+
),
129+
ProblemHighlightType.WARNING
130+
);
129131
}
130132
};
131133
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4+
<preference for="Magento\Catalog\Api\ProductRepositoryInterface" type="Foo\Bar\Model\Logger"/>
5+
</config>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4+
<preference for="" type="Not\Existent\Class"/>
5+
</config>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4+
<preference for="" type="Not\Existent\Class"/>
5+
</config>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4+
<preference for="" type="Foo\Bar\Model\Logger"/>
5+
</config>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4+
<preference for="Foo\Bar\Model\Logger" type=""/>
5+
</config>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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.magento.idea.magento2plugin.magento.files.ModuleDiXml;
9+
10+
public class PreferenceDeclarationInspectionTest extends InspectionXmlFixtureTestCase {
11+
12+
private static final String ARGUMENT_VALUE_IS_EMPTY =
13+
"inspection.error.idAttributeCanNotBeEmpty";
14+
15+
private static final String CLASS_DOES_NOT_EXIST =
16+
"inspection.warning.class.does.not.exist";
17+
18+
private static final String EXISTENT_CLASS_ONE =
19+
"Magento\\Catalog\\Api\\ProductRepositoryInterface";
20+
21+
private static final String EXISTENT_CLASS_TWO =
22+
"Foo\\Bar\\Model\\Logger";
23+
24+
private static final String NOT_EXISTENT_CLASS =
25+
"Not\\Existent\\Class";
26+
27+
@Override
28+
public void setUp() throws Exception {
29+
super.setUp();
30+
myFixture.enableInspections(PreferenceDeclarationInspection.class);
31+
}
32+
33+
/**
34+
* Test for the absence of an error in the presence of
35+
* classes or interfaces specified for preferences
36+
* <preference for="Magento\Catalog\Api\ProductRepositoryInterface" type="Foo\Bar\Model\Logger"/>
37+
*/
38+
public void testClassAttrForAndTypeAreExist() {
39+
configureFixture();
40+
41+
final String classOneExists = inspectionBundle.message(
42+
CLASS_DOES_NOT_EXIST,
43+
EXISTENT_CLASS_ONE
44+
);
45+
46+
assertHasNoHighlighting(classOneExists);
47+
48+
final String classTwoExists = inspectionBundle.message(
49+
CLASS_DOES_NOT_EXIST,
50+
EXISTENT_CLASS_TWO
51+
);
52+
53+
assertHasNoHighlighting(classTwoExists);
54+
}
55+
56+
/**
57+
* Test for throwing an error for a class that does not exist for the "for" attribute
58+
*/
59+
public void testClassAttrForDoesNotExists() {
60+
configureFixture();
61+
62+
final String forClassDoesNotExists = inspectionBundle.message(
63+
CLASS_DOES_NOT_EXIST,
64+
NOT_EXISTENT_CLASS
65+
);
66+
67+
assertHasHighlighting(forClassDoesNotExists);
68+
}
69+
70+
/**
71+
* Test for throwing an error for a class that does not exist for the "type" attribute
72+
*/
73+
public void testClassAttrTypeDoesNotExists() {
74+
configureFixture();
75+
76+
final String forClassDoesNotExists = inspectionBundle.message(
77+
CLASS_DOES_NOT_EXIST,
78+
NOT_EXISTENT_CLASS
79+
);
80+
81+
assertHasHighlighting(forClassDoesNotExists);
82+
}
83+
84+
/**
85+
* Test for an error for the "type" attribute because it is empty and
86+
* there is no error "for" the for attribute because this class exists
87+
* <preference for="Foo\Bar\Model\Logger" type=""/>
88+
*/
89+
public void testForAttrArgsValuesIsEmpty() {
90+
configureFixture();
91+
92+
final String forAttrIsEmptyMessage = inspectionBundle.message(
93+
ARGUMENT_VALUE_IS_EMPTY,
94+
ModuleDiXml.PREFERENCE_ATTR_FOR
95+
);
96+
97+
assertHasHighlighting(forAttrIsEmptyMessage);
98+
99+
final String typeAttrIsEmptyMessage = inspectionBundle.message(
100+
ARGUMENT_VALUE_IS_EMPTY,
101+
ModuleDiXml.TYPE_ATTR
102+
);
103+
104+
assertHasNoHighlighting(typeAttrIsEmptyMessage);
105+
}
106+
107+
/**
108+
* Test for an error for the "for" attribute because it is empty and
109+
* there is no error "type" the for attribute because this class exists
110+
* <preference for="" type="Foo\Bar\Model\Logger"/>
111+
*/
112+
public void testTypeAttrArgsValuesIsEmpty() {
113+
configureFixture();
114+
115+
final String forAttrIsEmptyMessage = inspectionBundle.message(
116+
ARGUMENT_VALUE_IS_EMPTY,
117+
ModuleDiXml.TYPE_ATTR
118+
);
119+
120+
assertHasHighlighting(forAttrIsEmptyMessage);
121+
122+
final String typeAttrIsEmptyMessage = inspectionBundle.message(
123+
ARGUMENT_VALUE_IS_EMPTY,
124+
ModuleDiXml.PREFERENCE_ATTR_FOR
125+
);
126+
127+
assertHasNoHighlighting(typeAttrIsEmptyMessage);
128+
}
129+
130+
private void configureFixture() {
131+
myFixture.configureByFile(getFixturePath(ModuleDiXml.FILE_NAME));
132+
}
133+
}

0 commit comments

Comments
 (0)