Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 47 additions & 20 deletions src/com/magento/idea/magento2plugin/indexes/EventIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.indexes;

import com.intellij.openapi.project.Project;
Expand All @@ -13,41 +14,67 @@
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.indexing.FileBasedIndex;
import com.magento.idea.magento2plugin.stubs.indexes.EventNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.EventObserverIndex;
import com.magento.idea.magento2plugin.util.xml.XmlPsiTreeUtil;

import java.util.ArrayList;
import java.util.Collection;

public class EventIndex {

private static EventIndex INSTANCE;

private Project project;

private EventIndex() {
}

public static EventIndex getInstance(final Project project) {
if (null == INSTANCE) {
INSTANCE = new EventIndex();
}
INSTANCE.project = project;
private final Project project;

return INSTANCE;
/**
* Constructor.
*/
public EventIndex(final Project project) {
this.project = project;
}

public Collection<PsiElement> getEventElements(final String name, final GlobalSearchScope scope) {
Collection<PsiElement> result = new ArrayList<>();
/**
* Gets event elements by event name.
*/
public Collection<PsiElement> getEventElements(
final String name,
final GlobalSearchScope scope
) {
final Collection<PsiElement> result = new ArrayList<>();

Collection<VirtualFile> virtualFiles =
final Collection<VirtualFile> virtualFiles =
FileBasedIndex.getInstance().getContainingFiles(EventNameIndex.KEY, name, scope);

for (VirtualFile virtualFile : virtualFiles) {
XmlFile xmlFile = (XmlFile) PsiManager.getInstance(project).findFile(virtualFile);
Collection<XmlAttributeValue> valueElements = XmlPsiTreeUtil
for (final VirtualFile virtualFile : virtualFiles) {
final XmlFile xmlFile = (XmlFile) PsiManager.getInstance(project).findFile(virtualFile);
final Collection<XmlAttributeValue> valueElements = XmlPsiTreeUtil
.findAttributeValueElements(xmlFile, "event", "name", name);
result.addAll(valueElements);
}
return result;
}

/**
* Gets observers by event-observer name combination.
*/
public Collection<PsiElement> getObservers(
final String eventName,
final String observerName,
final GlobalSearchScope scope
) {
final Collection<PsiElement> result = new ArrayList<>();
final Collection<VirtualFile> virtualFiles
= FileBasedIndex.getInstance().getContainingFiles(
EventObserverIndex.KEY, eventName, scope
);

for (final VirtualFile virtualFile: virtualFiles) {
final XmlFile eventsXmlFile
= (XmlFile) PsiManager.getInstance(project).findFile(virtualFile);
if (eventsXmlFile != null) {
result.addAll(
XmlPsiTreeUtil.findObserverTags(eventsXmlFile, eventName, observerName)
);
}
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class ObserverDeclarationInspection extends PhpInspection {

@NotNull
@Override
@SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops"})
public PsiElementVisitor buildVisitor(
final @NotNull ProblemsHolder problemsHolder,
final boolean isOnTheFly
Expand All @@ -66,12 +67,12 @@ public void visitFile(final PsiFile file) {
return;
}

final EventIndex eventIndex = EventIndex.getInstance(file.getProject());
final EventIndex eventIndex = new EventIndex(file.getProject());
final HashMap<String, XmlTag> targetObserversHash = new HashMap<>();

for (final XmlTag eventXmlTag: xmlTags) {
final HashMap<String, XmlTag> eventProblems = new HashMap<>();
if (!eventXmlTag.getName().equals("event")) {
if (!eventXmlTag.getName().equals(ModuleEventsXml.EVENT_TAG)) {
continue;
}

Expand Down Expand Up @@ -236,7 +237,7 @@ private List<XmlTag> fetchObserverTagsFromEventTag(final XmlTag eventXmlTag) {
}

for (final XmlTag observerXmlTag: observerXmlTags) {
if (!observerXmlTag.getName().equals("observer")) {
if (!observerXmlTag.getName().equals(ModuleEventsXml.OBSERVER_TAG)) {
continue;
}

Expand All @@ -257,10 +258,11 @@ private void addModuleNameWhereSameObserverUsed(
return;
}

if (!moduleDeclarationTag.getName().equals("module")) {
if (!moduleDeclarationTag.getName().equals(ModuleEventsXml.MODULE_TAG)) {
return;
}
final XmlAttribute moduleNameAttribute = moduleDeclarationTag.getAttribute("name");
final XmlAttribute moduleNameAttribute
= moduleDeclarationTag.getAttribute(ModuleEventsXml.NAME_ATTRIBUTE);
if (moduleNameAttribute == null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public class ModuleEventsXml implements ModuleFileInterface {
public static final String OBSERVER_TAG = "observer";
public static final String INSTANCE_ATTRIBUTE = "instance";
public static final String EVENT_TAG = "event";
public static final String MODULE_TAG = "module";

//attributes
public static final String NAME_ATTRIBUTE = "name";

private static final ModuleEventsXml INSTANCE = new ModuleEventsXml();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public PsiReference[] getReferencesByElement(
) {
final String value = StringUtil.unquoteString(element.getText());
final List<PsiReference> psiReferences = new ArrayList<>();
final Collection<PsiElement> targets = EventIndex.getInstance(element.getProject())
final Collection<PsiElement> targets = new EventIndex(element.getProject())
.getEventElements(
value,
GlobalSearchScope.getScopeRestrictedByFileTypes(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.magento.idea.magento2plugin.reference.provider;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.impl.source.xml.XmlAttributeValueImpl;
import com.intellij.psi.impl.source.xml.XmlTagImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ProcessingContext;
import com.magento.idea.magento2plugin.indexes.EventIndex;
import com.magento.idea.magento2plugin.reference.xml.PolyVariantReferenceBase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.NotNull;

public class ObserverNameReferenceProvider extends PsiReferenceProvider {
@Override
public PsiReference @NotNull [] getReferencesByElement(
@NotNull final PsiElement element,
@NotNull final ProcessingContext context
) {
final List<PsiReference> psiReferences = new ArrayList<>();
final XmlTagImpl eventTag = (XmlTagImpl) element.getParent().getParent().getParent();
final String eventName = eventTag.getAttributeValue("name");

if (eventName == null) {
return psiReferences.toArray(new PsiReference[0]);
}

final String observerName = ((XmlAttributeValueImpl) element).getValue();
final Collection<PsiElement> observers
= new EventIndex(element.getProject()).getObservers(
eventName, observerName, GlobalSearchScope.allScope(element.getProject())
);

if (!observers.isEmpty()) {
psiReferences.add(new PolyVariantReferenceBase(element, observers));
}

return psiReferences.toArray(new PsiReference[0]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
new EventNameReferenceProvider()
);

// <observer name="reference" />
registrar.registerReferenceProvider(
XmlPatterns.xmlAttributeValue().withParent(
XmlPatterns.xmlAttribute().withName("name").withParent(
XmlPatterns.xmlTag().withName("observer")
)
),
new ObserverNameReferenceProvider()
);

// <someXmlTag someAttribute="Module_Name[.*]" />
registrar.registerReferenceProvider(
XmlPatterns.xmlAttributeValue().withValue(string().matches(".*[A-Z][a-zA-Z0-9]+_[A-Z][a-zA-Z0-9]+.*")),
Expand Down
50 changes: 50 additions & 0 deletions src/com/magento/idea/magento2plugin/util/xml/XmlPsiTreeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
"PMD.UseObjectForClearerAPI"
})
public class XmlPsiTreeUtil {
public static final String EVENT_TAG_NAME = "event";
public static final String OBSERVER_TAG_NAME = "observer";
public static final String NAME_ATTRIBUTE = "name";

/**
* Get type tag of an argument.
*
Expand All @@ -47,6 +51,52 @@ public static XmlTag getTypeTagOfArgument(final XmlElement psiArgumentValueEleme
return PsiTreeUtil.getParentOfType(argumentsTag, XmlTag.class);
}

/**
* Finds observer tags by event-observer name combination.
*/
@SuppressWarnings({
"PMD.CyclomaticComplexity"
})
public static Collection<XmlAttributeValue> findObserverTags(
final XmlFile xmlFile,
final String eventName,
final String observerName
) {
Collection<XmlAttributeValue> psiElements = new THashSet<>();
final XmlTag configTag = xmlFile.getRootTag();

if (configTag == null) {
return psiElements;
}

/* Loop through event tags */
for (XmlTag eventTag: configTag.getSubTags()) {
XmlAttribute eventNameAttribute = eventTag.getAttribute(NAME_ATTRIBUTE);

/* Check if event tag and name matches */
if (EVENT_TAG_NAME.equals(eventTag.getName())
&& eventNameAttribute != null
&& eventName.equals(eventNameAttribute.getValue())
) {
/* Loop through observer tags under matched event tag */
for (XmlTag observerTag: eventTag.getSubTags()) {
XmlAttribute observerNameAttribute = observerTag.getAttribute(NAME_ATTRIBUTE);

/* Check if observer tag and name matches */
if (OBSERVER_TAG_NAME.equals(observerTag.getName())
&& observerNameAttribute != null
&& observerNameAttribute.getValueElement() != null
&& observerName.equals(observerNameAttribute.getValue())
) {
psiElements.add(observerNameAttribute.getValueElement());
}
}
}
}

return psiElements;
}

/**
* Find attribute value elements based on the tag name.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* See COPYING.txt for license details.
*/
-->

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="test_event_in_test_class">
<observer name="test_observer" instance="Magento\Catalog\Observer\TestObserver" />
</event>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="test_event_in_test_class">
<observer name="test_observer<caret>" disabled="true"/>
</event>
</config>
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,29 @@ protected void setUp() throws Exception {
protected void assertHasReferenceToXmlAttributeValue(final String reference) {
final PsiElement element = getElementFromCaret();
for (final PsiReference psiReference: element.getReferences()) {
final PsiElement resolved = psiReference.resolve();
if (!(resolved instanceof XmlAttributeValue)) {
continue;
}
if (psiReference instanceof PolyVariantReferenceBase) {
final ResolveResult[] resolveResults
= ((PolyVariantReferenceBase) psiReference).multiResolve(true);

if (((XmlAttributeValue) resolved).getValue().equals(reference)) {
return;
for (final ResolveResult resolveResult : resolveResults) {
final PsiElement resolved = resolveResult.getElement();
if (!(resolved instanceof XmlAttributeValue)) {
continue;
}

if (((XmlAttributeValue) resolved).getValue().equals(reference)) {
return;
}
}
} else {
final PsiElement resolved = psiReference.resolve();
if (!(resolved instanceof XmlAttributeValue)) {
continue;
}

if (((XmlAttributeValue) resolved).getValue().equals(reference)) {
return;
}
}
}
final String referenceNotFound =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ public void testObserverInstanceDirectorySnakeCaseMustHaveReference() {
assertHasReferencePhpClass("Magento\\Catalog\\test_event\\TestObserver");
}

/**
* Tests for observer name reference in events.xml.
*/
public void testObserverNameMustHaveReference() {
myFixture.configureByFile(this.getFixturePath(ModuleEventsXml.FILE_NAME));

assertHasReferenceToXmlAttributeValue("test_observer");
}

/**
* Tests for event name reference in events.xml.
*/
Expand Down