diff --git a/src/com/magento/idea/magento2plugin/linemarker/php/PluginLineMarkerProvider.java b/src/com/magento/idea/magento2plugin/linemarker/php/PluginLineMarkerProvider.java index 9b9724b8e..980e76d1b 100644 --- a/src/com/magento/idea/magento2plugin/linemarker/php/PluginLineMarkerProvider.java +++ b/src/com/magento/idea/magento2plugin/linemarker/php/PluginLineMarkerProvider.java @@ -2,6 +2,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + package com.magento.idea.magento2plugin.linemarker.php; import com.intellij.codeInsight.daemon.LineMarkerInfo; @@ -17,33 +18,49 @@ import com.jetbrains.php.lang.psi.elements.PhpClass; import com.magento.idea.magento2plugin.project.Settings; import com.magento.idea.magento2plugin.stubs.indexes.PluginIndex; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.commons.lang.WordUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; - public class PluginLineMarkerProvider implements LineMarkerProvider { - @Nullable + + private static final String TOOLTIP_TEXT = "Navigate to plugins"; + private static final int MIN_PLUGIN_METHOD_NAME_LENGTH = 6; + @Override - public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) { + public @Nullable LineMarkerInfo getLineMarkerInfo(final @NotNull PsiElement psiElement) { return null; } @Override - public void collectSlowLineMarkers(@NotNull List psiElements, @NotNull Collection> collection) { - if (psiElements.size() > 0) { - if (!Settings.isEnabled(psiElements.get(0).getProject())) { - return; - } + public void collectSlowLineMarkers( + final @NotNull List psiElements, + final @NotNull Collection> collection + ) { + if (psiElements.isEmpty()) { + return; } - PluginClassCache pluginClassCache = new PluginClassCache(); - ClassPluginCollector classPluginCollector = new ClassPluginCollector(pluginClassCache); - MethodPluginCollector methodPluginCollector = new MethodPluginCollector(pluginClassCache); - for (PsiElement psiElement : psiElements) { + if (!Settings.isEnabled(psiElements.get(0).getProject())) { + return; + } + final PluginClassCache pluginClassCache = new PluginClassCache(); + final ClassPluginCollector classPluginCollector = new ClassPluginCollector( + pluginClassCache + ); + final MethodPluginCollector methodPluginCollector = new MethodPluginCollector( + pluginClassCache + ); + + for (final PsiElement psiElement : psiElements) { if (psiElement instanceof PhpClass || psiElement instanceof Method) { - List results; + final List results; if (psiElement instanceof PhpClass) { results = classPluginCollector.collect((PhpClass) psiElement); @@ -51,12 +68,13 @@ public void collectSlowLineMarkers(@NotNull List psiElemen results = methodPluginCollector.collect((Method) psiElement); } - if (results.size() > 0 ) { - collection.add(NavigationGutterIconBuilder - .create(AllIcons.Nodes.Plugin) - .setTargets(results) - .setTooltipText("Navigate to plugins") - .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) + if (!results.isEmpty()) { + collection.add( + NavigationGutterIconBuilder + .create(AllIcons.Nodes.Plugin) + .setTargets(results) + .setTooltipText(TOOLTIP_TEXT) + .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) ); } } @@ -64,109 +82,140 @@ public void collectSlowLineMarkers(@NotNull List psiElemen } private static class PluginClassCache { - private HashMap> classPluginsMap = new HashMap>(); - List getPluginsForClass(@NotNull PhpClass phpClass, @NotNull String classFQN) { - List results = new ArrayList<>(); + private final Map> classPluginsMap = new HashMap<>(); + + public List getPluginsForClass(final @NotNull PhpClass phpClass) { + final List pluginsForClass = getPluginsForClass( + phpClass, + phpClass.getPresentableFQN() + ); + for (final PhpClass parent : phpClass.getSupers()) { + if (classPluginsMap.containsKey(parent.getFQN().substring(1))) { + continue; + } + pluginsForClass.addAll(getPluginsForClass(parent)); + } + + return pluginsForClass; + } + + public List getPluginsForClass( + final @NotNull PhpClass phpClass, + final @NotNull String classFQN + ) { if (classPluginsMap.containsKey(classFQN)) { return classPluginsMap.get(classFQN); } - List> plugins = FileBasedIndex.getInstance() - .getValues(PluginIndex.KEY, classFQN, GlobalSearchScope.allScope(phpClass.getProject())); + final List> plugins = FileBasedIndex.getInstance() + .getValues( + PluginIndex.KEY, + classFQN, + GlobalSearchScope.allScope(phpClass.getProject()) + ); + final List results = new ArrayList<>(); - if (plugins.size() == 0) { + if (plugins.isEmpty()) { classPluginsMap.put(classFQN, results); + return results; } + final PhpIndex phpIndex = PhpIndex.getInstance(phpClass.getProject()); - PhpIndex phpIndex = PhpIndex.getInstance(phpClass.getProject()); - - for (Set pluginClassNames: plugins) { - for (String pluginClassName: pluginClassNames) { + for (final Set pluginClassNames : plugins) { + for (final String pluginClassName: pluginClassNames) { results.addAll(phpIndex.getClassesByFQN(pluginClassName)); } } classPluginsMap.put(classFQN, results); + return results; } - List getPluginsForClass(@NotNull PhpClass phpClass) - { - List pluginsForClass = getPluginsForClass(phpClass, phpClass.getPresentableFQN()); - for (PhpClass parent: phpClass.getSupers()) { - pluginsForClass.addAll(getPluginsForClass(parent)); + public List getPluginMethods(final List plugins) { + final List methodList = new ArrayList<>(); + + for (final PhpClass plugin: plugins) { + methodList.addAll(getPluginMethods(plugin)); } - return pluginsForClass; + return methodList; } - List getPluginMethods(@NotNull PhpClass plugin) { - List methodList = new ArrayList(); - for (Method method : plugin.getMethods()) { + public List getPluginMethods(final @NotNull PhpClass pluginClass) { + final List methodList = new ArrayList<>(); + + for (final Method method : pluginClass.getMethods()) { if (method.getAccess().isPublic()) { - String pluginMethodName = method.getName(); - if (pluginMethodName.length() > 6) { + final String pluginMethodName = method.getName(); + + if (pluginMethodName.length() > MIN_PLUGIN_METHOD_NAME_LENGTH) { methodList.add(method); } } } - return methodList; - } - List getPluginMethods(List plugins) { - List methodList = new ArrayList(); - for (PhpClass plugin: plugins) { - methodList.addAll(getPluginMethods(plugin)); - } return methodList; } } private static class ClassPluginCollector implements Collector { - private PluginLineMarkerProvider.PluginClassCache pluginClassCache; - ClassPluginCollector(PluginLineMarkerProvider.PluginClassCache pluginClassCache) { + private final PluginLineMarkerProvider.PluginClassCache pluginClassCache; + + public ClassPluginCollector( + final PluginLineMarkerProvider.PluginClassCache pluginClassCache + ) { this.pluginClassCache = pluginClassCache; } @Override - public List collect(@NotNull PhpClass psiElement) { + public List collect(final @NotNull PhpClass psiElement) { return pluginClassCache.getPluginsForClass(psiElement); } } private static class MethodPluginCollector implements Collector { - private PluginLineMarkerProvider.PluginClassCache pluginClassCache; - MethodPluginCollector(PluginLineMarkerProvider.PluginClassCache pluginClassCache) { + private final PluginLineMarkerProvider.PluginClassCache pluginClassCache; + + public MethodPluginCollector( + final PluginLineMarkerProvider.PluginClassCache pluginClassCache + ) { this.pluginClassCache = pluginClassCache; } @Override - public List collect(@NotNull Method psiElement) { - List results = new ArrayList<>(); + public List collect(final @NotNull Method psiElement) { + final List results = new ArrayList<>(); + + final PhpClass methodClass = psiElement.getContainingClass(); - PhpClass methodClass = psiElement.getContainingClass(); if (methodClass == null) { return results; } + final List pluginsList = pluginClassCache.getPluginsForClass(methodClass); + final List pluginMethods = pluginClassCache.getPluginMethods(pluginsList); - List pluginsList = pluginClassCache.getPluginsForClass(methodClass); - List pluginMethods = pluginClassCache.getPluginMethods(pluginsList); + final String classMethodName = WordUtils.capitalize(psiElement.getName()); - String classMethodName = WordUtils.capitalize(psiElement.getName()); - for (Method pluginMethod: pluginMethods) { + for (final Method pluginMethod: pluginMethods) { if (isPluginMethodName(pluginMethod.getName(), classMethodName)) { results.add(pluginMethod); } } + return results; } - private boolean isPluginMethodName(String pluginMethodName, String classMethodName) { - return pluginMethodName.substring(5).equals(classMethodName) || pluginMethodName.substring(6).equals(classMethodName); + private boolean isPluginMethodName( + final String pluginMethodName, + final String classMethodName + ) { + return pluginMethodName.substring(5).equals(classMethodName) + || pluginMethodName.substring(6).equals(classMethodName); } } diff --git a/src/com/magento/idea/magento2plugin/linemarker/php/PluginTargetLineMarkerProvider.java b/src/com/magento/idea/magento2plugin/linemarker/php/PluginTargetLineMarkerProvider.java index b4de80de2..5f63c8620 100644 --- a/src/com/magento/idea/magento2plugin/linemarker/php/PluginTargetLineMarkerProvider.java +++ b/src/com/magento/idea/magento2plugin/linemarker/php/PluginTargetLineMarkerProvider.java @@ -21,13 +21,17 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class PluginTargetLineMarkerProvider implements LineMarkerProvider { - @Nullable + + private static final String TARGET_CLASS_TOOLTIP_TEXT = "Navigate to target class"; + private static final String TARGET_METHOD_TOOLTIP_TEXT = "Navigate to target method"; + @Override - public LineMarkerInfo getLineMarkerInfo(@NotNull final PsiElement psiElement) { + public @Nullable LineMarkerInfo getLineMarkerInfo(final @NotNull PsiElement psiElement) { return null; } @@ -36,48 +40,52 @@ public void collectSlowLineMarkers( final @NotNull List psiElements, final @NotNull Collection> collection ) { - if (!psiElements.isEmpty() && !Settings.isEnabled(psiElements.get(0).getProject())) { + if (psiElements.isEmpty()) { + return; + } + + if (!Settings.isEnabled(psiElements.get(0).getProject())) { return; } final PluginClassCache pluginClassCache = new PluginClassCache(); - final TargetClassesCollector targetClassesCollector = - new TargetClassesCollector(pluginClassCache); - final TargetMethodsCollector targetMethodsCollector = - new TargetMethodsCollector(pluginClassCache); + final TargetClassesCollector targetClassesCollector = new TargetClassesCollector( + pluginClassCache + ); + final TargetMethodsCollector targetMethodsCollector = new TargetMethodsCollector( + pluginClassCache + ); for (final PsiElement psiElement : psiElements) { - if (psiElement instanceof PhpClass || psiElement instanceof Method) { - List results; - - if (psiElement instanceof PhpClass) { - results = targetClassesCollector.collect((PhpClass) psiElement); - if (!results.isEmpty()) { - collection.add(NavigationGutterIconBuilder - .create(AllIcons.Nodes.Class) - .setTargets(results) - .setTooltipText("Navigate to target class") - .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) - ); - } - } else { - results = targetMethodsCollector.collect((Method) psiElement); - if (!results.isEmpty()) { - collection.add(NavigationGutterIconBuilder - .create(AllIcons.Nodes.Method) - .setTargets(results) - .setTooltipText("Navigate to target method") - .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) - ); - } + if (psiElement instanceof PhpClass) { + final List results = + targetClassesCollector.collect((PhpClass) psiElement); + + if (!results.isEmpty()) { + collection.add(NavigationGutterIconBuilder + .create(AllIcons.Nodes.Class) + .setTargets(results) + .setTooltipText(TARGET_CLASS_TOOLTIP_TEXT) + .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) + ); + } + } else if (psiElement instanceof Method) { + final List results = + targetMethodsCollector.collect((Method) psiElement); + + if (!results.isEmpty()) { + collection.add(NavigationGutterIconBuilder + .create(AllIcons.Nodes.Method) + .setTargets(results) + .setTooltipText(TARGET_METHOD_TOOLTIP_TEXT) + .createLineMarkerInfo(PsiTreeUtil.getDeepestFirst(psiElement)) + ); } - } } } private static class PluginClassCache { - private final HashMap> pluginClassesMap = // NOPMD - new HashMap<>(); + private final Map> pluginClassesMap = new HashMap<>(); private List getTargetClassesForPlugin( @NotNull final PhpClass phpClass, @@ -119,6 +127,9 @@ protected List getTargetClassesForPlugin(@NotNull final PhpClass phpCl phpClass, phpClass.getPresentableFQN() ); for (final PhpClass parent: phpClass.getSupers()) { + if (pluginClassesMap.containsKey(parent.getFQN().substring(1))) { + continue; + } classesForPlugin.addAll(getTargetClassesForPlugin(parent)); } @@ -127,9 +138,10 @@ protected List getTargetClassesForPlugin(@NotNull final PhpClass phpCl } private static class TargetClassesCollector implements Collector { + private final PluginTargetLineMarkerProvider.PluginClassCache pluginClassCache; - TargetClassesCollector(// NOPMD + public TargetClassesCollector( final PluginTargetLineMarkerProvider.PluginClassCache pluginClassCache ) { this.pluginClassCache = pluginClassCache; @@ -142,9 +154,10 @@ public List collect(@NotNull final PhpClass psiElement) { } private static class TargetMethodsCollector implements Collector { + private final PluginTargetLineMarkerProvider.PluginClassCache pluginClassCache; - TargetMethodsCollector(// NOPMD + public TargetMethodsCollector( final PluginTargetLineMarkerProvider.PluginClassCache pluginClassCache ) { this.pluginClassCache = pluginClassCache; @@ -158,32 +171,33 @@ public List collect(@NotNull final Method pluginMethod) { if (null == getPluginPrefix(pluginMethod)) { return results; } - final PhpClass pluginClass = pluginMethod.getContainingClass(); + if (pluginClass == null) { return results; } final List targetClasses = pluginClassCache.getTargetClassesForPlugin(pluginClass); + if (targetClasses.isEmpty()) { return results; } - for (final PhpClass targetClass: targetClasses) { + for (final PhpClass targetClass : targetClasses) { final String pluginPrefix = getPluginPrefix(pluginMethod); final String targetClassMethodName = getTargetMethodName( pluginMethod, pluginPrefix ); + if (targetClassMethodName == null) { continue; } - final Method targetMethod = targetClass.findMethodByName(targetClassMethodName); + if (targetMethod == null) { continue; } - results.add(targetMethod); } @@ -192,25 +206,31 @@ public List collect(@NotNull final Method pluginMethod) { private String getTargetMethodName(final Method pluginMethod, final String pluginPrefix) { final String targetClassMethodName = pluginMethod.getName().replace(pluginPrefix, ""); + if (targetClassMethodName.isEmpty()) { return null; } final char firstCharOfTargetName = targetClassMethodName.charAt(0); + if (Character.getType(firstCharOfTargetName) == Character.LOWERCASE_LETTER) { return null; } + return Character.toLowerCase(firstCharOfTargetName) + targetClassMethodName.substring(1); } private String getPluginPrefix(final Method pluginMethod) { final String pluginMethodName = pluginMethod.getName(); + if (pluginMethodName.startsWith(Plugin.PluginType.around.toString())) { return Plugin.PluginType.around.toString(); } + if (pluginMethodName.startsWith(Plugin.PluginType.before.toString())) { return Plugin.PluginType.before.toString(); } + if (pluginMethodName.startsWith(Plugin.PluginType.after.toString())) { return Plugin.PluginType.after.toString(); } diff --git a/src/com/magento/idea/magento2plugin/linemarker/php/WebApiLineMarkerProvider.java b/src/com/magento/idea/magento2plugin/linemarker/php/WebApiLineMarkerProvider.java index b4d2f012e..dda93f5bb 100644 --- a/src/com/magento/idea/magento2plugin/linemarker/php/WebApiLineMarkerProvider.java +++ b/src/com/magento/idea/magento2plugin/linemarker/php/WebApiLineMarkerProvider.java @@ -1,7 +1,8 @@ -/** +/* * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + package com.magento.idea.magento2plugin.linemarker.php; import com.intellij.codeInsight.daemon.LineMarkerInfo; @@ -15,11 +16,14 @@ import com.magento.idea.magento2plugin.MagentoIcons; import com.magento.idea.magento2plugin.project.Settings; import com.magento.idea.magento2plugin.stubs.indexes.WebApiTypeIndex; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; - /** * Line marker for methods and classes which are exposed as web APIs. *

@@ -28,38 +32,47 @@ */ public class WebApiLineMarkerProvider implements LineMarkerProvider { - @Nullable @Override - public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) { + public @Nullable LineMarkerInfo getLineMarkerInfo(final @NotNull PsiElement psiElement) { return null; } + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") @Override - public void collectSlowLineMarkers(@NotNull List psiElements, @NotNull Collection> collection) { - if (psiElements.size() > 0) { - if (!Settings.isEnabled(psiElements.get(0).getProject())) { - return; - } + public void collectSlowLineMarkers( + final @NotNull List psiElements, + final @NotNull Collection> collection + ) { + if (psiElements.isEmpty()) { + return; } - for (PsiElement psiElement: psiElements) { - WebApiRoutesCollector collector = new WebApiRoutesCollector(); + + if (!Settings.isEnabled(psiElements.get(0).getProject())) { + return; + } + final WebApiRoutesCollector collector = new WebApiRoutesCollector(); + + for (final PsiElement psiElement : psiElements) { List results = new ArrayList<>(); + if (psiElement instanceof Method) { results = collector.getRoutes((Method) psiElement); } else if (psiElement instanceof PhpClass) { results = collector.getRoutes((PhpClass) psiElement); } - if (!(results.size() > 0)) { + if (results.isEmpty()) { continue; } - StringBuilder tooltipText = new StringBuilder("Navigate to Web API configuration:

");
-            for (XmlTag routeTag : results) {
-                tooltipText.append(routeTag.getName()).append("\n");
+            final StringBuilder tooltipText = new StringBuilder(
+                    "Navigate to Web API configuration:
"
+            );
+            for (final XmlTag routeTag : results) {
+                tooltipText.append(routeTag.getName()).append('\n');
             }
             tooltipText.append("
"); - NavigationGutterIconBuilder builder = NavigationGutterIconBuilder + final NavigationGutterIconBuilder builder = NavigationGutterIconBuilder .create(MagentoIcons.WEB_API) .setTargets(results) .setTooltipText(tooltipText.toString()); @@ -72,24 +85,28 @@ public void collectSlowLineMarkers(@NotNull List psiElemen */ private static class WebApiRoutesCollector { - private HashMap> routesCache = new HashMap<>(); + private final Map> routesCache = new HashMap<>(); + private final Map httpMethodsSortOrder; - private static final Map HTTP_METHODS_SORT_ORDER = new HashMap() {{ - put("GET", 1); - put("PUT", 2); - put("POS", 3); - put("DEL", 4); - }}; + public WebApiRoutesCollector() { + httpMethodsSortOrder = new HashMap<>(); + httpMethodsSortOrder.put("GET", 1); + httpMethodsSortOrder.put("PUT", 2); + httpMethodsSortOrder.put("POS", 3); + httpMethodsSortOrder.put("DEL", 4); + } /** * Get sorted list of Web API routes related to the specified class. */ - List getRoutes(@NotNull PhpClass phpClass) { - List routesForClass = new ArrayList<>(); - for (Method method : phpClass.getMethods()) { + public List getRoutes(final @NotNull PhpClass phpClass) { + final List routesForClass = new ArrayList<>(); + + for (final Method method : phpClass.getMethods()) { routesForClass.addAll(getRoutes(method)); } sortRoutes(routesForClass); + return routesForClass; } @@ -98,13 +115,15 @@ List getRoutes(@NotNull PhpClass phpClass) { *

* Results are cached. */ - List getRoutes(@NotNull Method method) { - String methodFqn = method.getFQN(); + public List getRoutes(final @NotNull Method method) { + final String methodFqn = method.getFQN(); + if (!routesCache.containsKey(methodFqn)) { - List routesForMethod = extractRoutesForMethod(method); + final List routesForMethod = extractRoutesForMethod(method); sortRoutes(routesForMethod); routesCache.put(methodFqn, routesForMethod); } + return routesCache.get(methodFqn); } @@ -114,37 +133,66 @@ List getRoutes(@NotNull Method method) { * Web API declarations for parent classes are taken into account. * Results are not cached. */ - List extractRoutesForMethod(@NotNull Method method) { - List routesForMethod = WebApiTypeIndex.getWebApiRoutes(method); - PhpClass phpClass = method.getContainingClass(); + public List extractRoutesForMethod(final @NotNull Method method) { + final List routesForMethod = new ArrayList<>(); + final Map> routesForMethodMap = new HashMap<>(); + + for (final Map.Entry> entry + : extractRoutesForMethodRecursively(method, routesForMethodMap).entrySet()) { + routesForMethod.addAll(entry.getValue()); + } + + return routesForMethod; + } + + private Map> extractRoutesForMethodRecursively( + final @NotNull Method method, + final Map> routesForMethod + ) { + routesForMethod.put(method.getFQN(), WebApiTypeIndex.getWebApiRoutes(method)); + final PhpClass phpClass = method.getContainingClass(); + if (phpClass == null) { return routesForMethod; } - for (PhpClass parent : method.getContainingClass().getSupers()) { - for (Method parentMethod : parent.getMethods()) { + + for (final PhpClass parent : method.getContainingClass().getSupers()) { + for (final Method parentMethod : parent.getMethods()) { if (parentMethod.getName().equals(method.getName())) { - routesForMethod.addAll(extractRoutesForMethod(parentMethod)); + if (routesForMethod.containsKey(parentMethod.getFQN())) { + continue; + } + routesForMethod.putAll( + extractRoutesForMethodRecursively( + parentMethod, + routesForMethod + ) + ); } } } + return routesForMethod; } /** * Make sure that routes are sorted as follows: GET, PUT, POST, DELETE. Then by path. */ - private void sortRoutes(List routes) { + private void sortRoutes(final List routes) { routes.sort( - (firstTag, secondTag) -> { - String substring = firstTag.getName().substring(2, 5); - Integer firstSortOrder = HTTP_METHODS_SORT_ORDER.get(substring); - Integer secondSortOrder = HTTP_METHODS_SORT_ORDER.get(secondTag.getName().substring(2, 5)); - if (firstSortOrder.compareTo(secondSortOrder) == 0) { - // Sort by route if HTTP methods are equal - return firstTag.getName().compareTo(secondTag.getName()); + (firstTag, secondTag) -> { + final String substring = firstTag.getName().substring(2, 5); + final Integer firstSortOrder = httpMethodsSortOrder.get(substring); + final Integer secondSortOrder = httpMethodsSortOrder.get( + secondTag.getName().substring(2, 5) + ); + if (firstSortOrder.compareTo(secondSortOrder) == 0) { + // Sort by route if HTTP methods are equal + return firstTag.getName().compareTo(secondTag.getName()); + } + + return firstSortOrder.compareTo(secondSortOrder); } - return firstSortOrder.compareTo(secondSortOrder); - } ); } }