Skip to content

Commit 7fd808b

Browse files
authored
Merge pull request #813 from bohdan-harniuk/bugfix/765-StackOverflowError-for-navigate-to-plugins-linemarker-provider
Bugfix/765: Fixed stack overflow error for line-marker providers
2 parents 3e0d7be + f452d79 commit 7fd808b

File tree

3 files changed

+264
-147
lines changed

3 files changed

+264
-147
lines changed

src/com/magento/idea/magento2plugin/linemarker/php/PluginLineMarkerProvider.java

Lines changed: 111 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright © Magento, Inc. All rights reserved.
33
* See COPYING.txt for license details.
44
*/
5+
56
package com.magento.idea.magento2plugin.linemarker.php;
67

78
import com.intellij.codeInsight.daemon.LineMarkerInfo;
@@ -17,156 +18,204 @@
1718
import com.jetbrains.php.lang.psi.elements.PhpClass;
1819
import com.magento.idea.magento2plugin.project.Settings;
1920
import com.magento.idea.magento2plugin.stubs.indexes.PluginIndex;
21+
import java.util.ArrayList;
22+
import java.util.Collection;
23+
import java.util.HashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Set;
2027
import org.apache.commons.lang.WordUtils;
2128
import org.jetbrains.annotations.NotNull;
2229
import org.jetbrains.annotations.Nullable;
2330

24-
import java.util.*;
25-
2631
public class PluginLineMarkerProvider implements LineMarkerProvider {
27-
@Nullable
32+
33+
private static final String TOOLTIP_TEXT = "Navigate to plugins";
34+
private static final int MIN_PLUGIN_METHOD_NAME_LENGTH = 6;
35+
2836
@Override
29-
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) {
37+
public @Nullable LineMarkerInfo<?> getLineMarkerInfo(final @NotNull PsiElement psiElement) {
3038
return null;
3139
}
3240

3341
@Override
34-
public void collectSlowLineMarkers(@NotNull List<? extends PsiElement> psiElements, @NotNull Collection<? super LineMarkerInfo<?>> collection) {
35-
if (psiElements.size() > 0) {
36-
if (!Settings.isEnabled(psiElements.get(0).getProject())) {
37-
return;
38-
}
42+
public void collectSlowLineMarkers(
43+
final @NotNull List<? extends PsiElement> psiElements,
44+
final @NotNull Collection<? super LineMarkerInfo<?>> collection
45+
) {
46+
if (psiElements.isEmpty()) {
47+
return;
3948
}
40-
PluginClassCache pluginClassCache = new PluginClassCache();
41-
ClassPluginCollector classPluginCollector = new ClassPluginCollector(pluginClassCache);
42-
MethodPluginCollector methodPluginCollector = new MethodPluginCollector(pluginClassCache);
4349

44-
for (PsiElement psiElement : psiElements) {
50+
if (!Settings.isEnabled(psiElements.get(0).getProject())) {
51+
return;
52+
}
53+
final PluginClassCache pluginClassCache = new PluginClassCache();
54+
final ClassPluginCollector classPluginCollector = new ClassPluginCollector(
55+
pluginClassCache
56+
);
57+
final MethodPluginCollector methodPluginCollector = new MethodPluginCollector(
58+
pluginClassCache
59+
);
60+
61+
for (final PsiElement psiElement : psiElements) {
4562
if (psiElement instanceof PhpClass || psiElement instanceof Method) {
46-
List<? extends PsiElement> results;
63+
final List<? extends PsiElement> results;
4764

4865
if (psiElement instanceof PhpClass) {
4966
results = classPluginCollector.collect((PhpClass) psiElement);
5067
} else {
5168
results = methodPluginCollector.collect((Method) psiElement);
5269
}
5370

54-
if (results.size() > 0 ) {
55-
collection.add(NavigationGutterIconBuilder
56-
.create(AllIcons.Nodes.Plugin)
57-
.setTargets(results)
58-
.setTooltipText("Navigate to plugins")
59-
.createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement))
71+
if (!results.isEmpty()) {
72+
collection.add(
73+
NavigationGutterIconBuilder
74+
.create(AllIcons.Nodes.Plugin)
75+
.setTargets(results)
76+
.setTooltipText(TOOLTIP_TEXT)
77+
.createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement))
6078
);
6179
}
6280
}
6381
}
6482
}
6583

6684
private static class PluginClassCache {
67-
private HashMap<String, List<PhpClass>> classPluginsMap = new HashMap<String, List<PhpClass>>();
6885

69-
List<PhpClass> getPluginsForClass(@NotNull PhpClass phpClass, @NotNull String classFQN) {
70-
List<PhpClass> results = new ArrayList<>();
86+
private final Map<String, List<PhpClass>> classPluginsMap = new HashMap<>();
87+
88+
public List<PhpClass> getPluginsForClass(final @NotNull PhpClass phpClass) {
89+
final List<PhpClass> pluginsForClass = getPluginsForClass(
90+
phpClass,
91+
phpClass.getPresentableFQN()
92+
);
7193

94+
for (final PhpClass parent : phpClass.getSupers()) {
95+
if (classPluginsMap.containsKey(parent.getFQN().substring(1))) {
96+
continue;
97+
}
98+
pluginsForClass.addAll(getPluginsForClass(parent));
99+
}
100+
101+
return pluginsForClass;
102+
}
103+
104+
public List<PhpClass> getPluginsForClass(
105+
final @NotNull PhpClass phpClass,
106+
final @NotNull String classFQN
107+
) {
72108
if (classPluginsMap.containsKey(classFQN)) {
73109
return classPluginsMap.get(classFQN);
74110
}
75111

76-
List<Set<String>> plugins = FileBasedIndex.getInstance()
77-
.getValues(PluginIndex.KEY, classFQN, GlobalSearchScope.allScope(phpClass.getProject()));
112+
final List<Set<String>> plugins = FileBasedIndex.getInstance()
113+
.getValues(
114+
PluginIndex.KEY,
115+
classFQN,
116+
GlobalSearchScope.allScope(phpClass.getProject())
117+
);
118+
final List<PhpClass> results = new ArrayList<>();
78119

79-
if (plugins.size() == 0) {
120+
if (plugins.isEmpty()) {
80121
classPluginsMap.put(classFQN, results);
122+
81123
return results;
82124
}
125+
final PhpIndex phpIndex = PhpIndex.getInstance(phpClass.getProject());
83126

84-
PhpIndex phpIndex = PhpIndex.getInstance(phpClass.getProject());
85-
86-
for (Set<String> pluginClassNames: plugins) {
87-
for (String pluginClassName: pluginClassNames) {
127+
for (final Set<String> pluginClassNames : plugins) {
128+
for (final String pluginClassName: pluginClassNames) {
88129
results.addAll(phpIndex.getClassesByFQN(pluginClassName));
89130
}
90131
}
91132
classPluginsMap.put(classFQN, results);
133+
92134
return results;
93135
}
94136

95-
List<PhpClass> getPluginsForClass(@NotNull PhpClass phpClass)
96-
{
97-
List<PhpClass> pluginsForClass = getPluginsForClass(phpClass, phpClass.getPresentableFQN());
98-
for (PhpClass parent: phpClass.getSupers()) {
99-
pluginsForClass.addAll(getPluginsForClass(parent));
137+
public List<Method> getPluginMethods(final List<PhpClass> plugins) {
138+
final List<Method> methodList = new ArrayList<>();
139+
140+
for (final PhpClass plugin: plugins) {
141+
methodList.addAll(getPluginMethods(plugin));
100142
}
101143

102-
return pluginsForClass;
144+
return methodList;
103145
}
104146

105-
List<Method> getPluginMethods(@NotNull PhpClass plugin) {
106-
List<Method> methodList = new ArrayList<Method>();
107-
for (Method method : plugin.getMethods()) {
147+
public List<Method> getPluginMethods(final @NotNull PhpClass pluginClass) {
148+
final List<Method> methodList = new ArrayList<>();
149+
150+
for (final Method method : pluginClass.getMethods()) {
108151
if (method.getAccess().isPublic()) {
109-
String pluginMethodName = method.getName();
110-
if (pluginMethodName.length() > 6) {
152+
final String pluginMethodName = method.getName();
153+
154+
if (pluginMethodName.length() > MIN_PLUGIN_METHOD_NAME_LENGTH) {
111155
methodList.add(method);
112156
}
113157
}
114158
}
115-
return methodList;
116-
}
117159

118-
List<Method> getPluginMethods(List<PhpClass> plugins) {
119-
List<Method> methodList = new ArrayList<Method>();
120-
for (PhpClass plugin: plugins) {
121-
methodList.addAll(getPluginMethods(plugin));
122-
}
123160
return methodList;
124161
}
125162
}
126163

127164
private static class ClassPluginCollector implements Collector<PhpClass, PhpClass> {
128-
private PluginLineMarkerProvider.PluginClassCache pluginClassCache;
129165

130-
ClassPluginCollector(PluginLineMarkerProvider.PluginClassCache pluginClassCache) {
166+
private final PluginLineMarkerProvider.PluginClassCache pluginClassCache;
167+
168+
public ClassPluginCollector(
169+
final PluginLineMarkerProvider.PluginClassCache pluginClassCache
170+
) {
131171
this.pluginClassCache = pluginClassCache;
132172
}
133173

134174
@Override
135-
public List<PhpClass> collect(@NotNull PhpClass psiElement) {
175+
public List<PhpClass> collect(final @NotNull PhpClass psiElement) {
136176
return pluginClassCache.getPluginsForClass(psiElement);
137177
}
138178
}
139179

140180
private static class MethodPluginCollector implements Collector<Method, Method> {
141-
private PluginLineMarkerProvider.PluginClassCache pluginClassCache;
142181

143-
MethodPluginCollector(PluginLineMarkerProvider.PluginClassCache pluginClassCache) {
182+
private final PluginLineMarkerProvider.PluginClassCache pluginClassCache;
183+
184+
public MethodPluginCollector(
185+
final PluginLineMarkerProvider.PluginClassCache pluginClassCache
186+
) {
144187
this.pluginClassCache = pluginClassCache;
145188
}
146189

147190
@Override
148-
public List<Method> collect(@NotNull Method psiElement) {
149-
List<Method> results = new ArrayList<>();
191+
public List<Method> collect(final @NotNull Method psiElement) {
192+
final List<Method> results = new ArrayList<>();
193+
194+
final PhpClass methodClass = psiElement.getContainingClass();
150195

151-
PhpClass methodClass = psiElement.getContainingClass();
152196
if (methodClass == null) {
153197
return results;
154198
}
199+
final List<PhpClass> pluginsList = pluginClassCache.getPluginsForClass(methodClass);
200+
final List<Method> pluginMethods = pluginClassCache.getPluginMethods(pluginsList);
155201

156-
List<PhpClass> pluginsList = pluginClassCache.getPluginsForClass(methodClass);
157-
List<Method> pluginMethods = pluginClassCache.getPluginMethods(pluginsList);
202+
final String classMethodName = WordUtils.capitalize(psiElement.getName());
158203

159-
String classMethodName = WordUtils.capitalize(psiElement.getName());
160-
for (Method pluginMethod: pluginMethods) {
204+
for (final Method pluginMethod: pluginMethods) {
161205
if (isPluginMethodName(pluginMethod.getName(), classMethodName)) {
162206
results.add(pluginMethod);
163207
}
164208
}
209+
165210
return results;
166211
}
167212

168-
private boolean isPluginMethodName(String pluginMethodName, String classMethodName) {
169-
return pluginMethodName.substring(5).equals(classMethodName) || pluginMethodName.substring(6).equals(classMethodName);
213+
private boolean isPluginMethodName(
214+
final String pluginMethodName,
215+
final String classMethodName
216+
) {
217+
return pluginMethodName.substring(5).equals(classMethodName)
218+
|| pluginMethodName.substring(6).equals(classMethodName);
170219
}
171220
}
172221

0 commit comments

Comments
 (0)