From 383164fc8bcba5a115ac5a15fd88ae2fe1afcad5 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Sat, 7 May 2016 09:54:59 +0200 Subject: [PATCH] refactoring route to use object serialization and force reindexing #725 --- .../idea/symfony2plugin/routing/Route.java | 32 ++++------ .../symfony2plugin/routing/RouteHelper.java | 32 +++++----- .../routing/RouteLookupElement.java | 27 +++++++- .../routing/dict/JsonRoute.java | 18 ++++++ .../routing/dict/RouteInterface.java | 5 ++ .../stubs/dict/StubIndexedRoute.java | 4 +- .../stubs/indexes/RoutesStubIndex.java | 64 +++++++++---------- .../tests/routing/RouteTest.java | 25 +++----- ...TestTest.java => RoutesStubIndexTest.java} | 26 +++++++- 9 files changed, 140 insertions(+), 93 deletions(-) rename tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/{RoutesStubIndexTestTest.java => RoutesStubIndexTest.java} (63%) diff --git a/src/fr/adrienbrault/idea/symfony2plugin/routing/Route.java b/src/fr/adrienbrault/idea/symfony2plugin/routing/Route.java index 2dbdeff59..476f80314 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/routing/Route.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/routing/Route.java @@ -1,7 +1,6 @@ package fr.adrienbrault.idea.symfony2plugin.routing; import fr.adrienbrault.idea.symfony2plugin.routing.dict.RouteInterface; -import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,9 +12,13 @@ * @author Adrien Brault * @author Daniel Espendiller */ -public class Route { +public class Route implements RouteInterface { + @NotNull final private String name; + + @NotNull + private Collection methods = new HashSet<>(); private String controller; private String path; private Set pathCache; @@ -25,7 +28,7 @@ public class Route { private HashMap requirements = new HashMap(); private List> tokens = new ArrayList>(); - public Route(String name, HashSet variables, HashMap defaults, HashMap requirements, ArrayList> tokens) { + public Route(@NotNull String name, HashSet variables, HashMap defaults, HashMap requirements, ArrayList> tokens) { this.name = name; this.variables = variables; @@ -50,23 +53,7 @@ public Route(@NotNull RouteInterface routeInterface) { this.name = routeInterface.getName(); this.controller = routeInterface.getController(); this.path = routeInterface.getPath(); - } - - public Route(@NotNull String name, @Nullable String[] indexed) { - this.name = name; - - // its not valid to provide nullable value, - // but index based allows this - if(indexed != null) { - if(indexed.length >= 1 && StringUtils.isNotBlank(indexed[0])) { - this.controller = indexed[0]; - } - - if(indexed.length >= 2 && StringUtils.isNotBlank(indexed[1])) { - this.path = indexed[1]; - } - } - + this.methods = routeInterface.getMethods(); } @NotNull @@ -118,4 +105,9 @@ public String getPath() { return path; } + @NotNull + @Override + public Collection getMethods() { + return this.methods; + } } diff --git a/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java b/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java index e1a73ccf9..9d49ae04a 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java @@ -87,22 +87,22 @@ public static LookupElement[] getRouteParameterLookupElements(Project project, S } @Nullable - public static Route getRoute(Project project, String routeName) { + public static Route getRoute(@NotNull Project project, @NotNull String routeName) { Map compiledRoutes = RouteHelper.getCompiledRoutes(project); - if(!compiledRoutes.containsKey(routeName)) { - - // @TODO: provide multiple ones - Collection foo = FileBasedIndex.getInstance().getContainingFiles(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project)); - for(String[] str: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.filesScope(project, foo))) { - return new Route(routeName, str); - } + if(compiledRoutes.containsKey(routeName)) { + return compiledRoutes.get(routeName); + } - return null; + // @TODO: provide multiple ones + Collection routeFiles = FileBasedIndex.getInstance().getContainingFiles(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project)); + for(StubIndexedRoute route: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.filesScope(project, routeFiles))) { + return new Route(route); } - return compiledRoutes.get(routeName); + return null; } + public static PsiElement[] getRouteParameterPsiElements(Project project, String routeName, String parameterName) { List results = new ArrayList(); @@ -633,7 +633,7 @@ public static Collection getYamlRouteDefinitions(@NotNull YAML String methods = YamlHelper.getStringValueOfKeyInProbablyMapping(element, "methods"); if(methods != null) { // value: [GET, POST, - String[] split = methods.replace("[", "").replace("]", "").replaceAll(" +", "").split(","); + String[] split = methods.replace("[", "").replace("]", "").replaceAll(" +", "").toLowerCase().split(","); if(split.length > 0) { route.addMethod(split); } @@ -690,7 +690,7 @@ public static Collection getXmlRouteDefinitions(XmlFile psiFil String methods = servicesTag.getAttributeValue("methods"); if(methods != null && StringUtils.isNotBlank(methods)) { - String[] split = methods.replaceAll(" +", " ").split(" "); + String[] split = methods.replaceAll(" +", "").toLowerCase().split("\\|"); if(split.length > 0) { e.addMethod(split); } @@ -915,8 +915,8 @@ public static List getRoutesLookupElements(final @NotNull Project if(uniqueSet.contains(routeName)) { continue; } - for(String[] splits: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project))) { - lookupElements.add(new RouteLookupElement(new Route(routeName, splits), true)); + for(StubIndexedRoute route: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project))) { + lookupElements.add(new RouteLookupElement(new Route(route), true)); uniqueSet.add(routeName); } } @@ -987,9 +987,9 @@ private static Map getAllRoutesProxy(@NotNull Project project) { continue; } - for(String[] splits: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project))) { + for(StubIndexedRoute route: FileBasedIndex.getInstance().getValues(RoutesStubIndex.KEY, routeName, GlobalSearchScope.allScope(project))) { uniqueKeySet.add(routeName); - routes.put(routeName, new Route(routeName, splits)); + routes.put(routeName, new Route(route)); } } diff --git a/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteLookupElement.java b/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteLookupElement.java index 0e5d0f268..89a5b66bf 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteLookupElement.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/routing/RouteLookupElement.java @@ -4,10 +4,14 @@ import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementPresentation; +import com.intellij.util.containers.ContainerUtil; import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Set; /** @@ -20,11 +24,11 @@ public class RouteLookupElement extends LookupElement { private boolean isWeak = false; private InsertHandler insertHandler; - public RouteLookupElement(Route route) { + public RouteLookupElement(@NotNull Route route) { this.route = route; } - public RouteLookupElement(Route route, boolean isWeak) { + public RouteLookupElement(@NotNull Route route, boolean isWeak) { this(route); this.isWeak = isWeak; } @@ -41,11 +45,28 @@ public void renderElement(LookupElementPresentation presentation) { presentation.setTypeGrayed(true); presentation.setIcon(!this.isWeak ? Symfony2Icons.ROUTE : Symfony2Icons.ROUTE_WEAK); + formatTail(presentation); + } + + /** + * "(GET|POST, var1, var2)" + */ + private void formatTail(@NotNull LookupElementPresentation presentation) { + List tails = new ArrayList<>(); + + Collection methods = route.getMethods(); + if(methods.size() > 0) { + tails.add(StringUtils.join(ContainerUtil.map(methods, String::toUpperCase), "|")); + } + Set variables = this.route.getVariables(); if(variables.size() > 0) { - presentation.setTailText("(" + StringUtils.join(variables, ", ") + ")", true); + tails.addAll(variables); } + if(tails.size() > 0) { + presentation.setTailText("(" + StringUtils.join(tails, ", ") + ")", true); + } } @Override diff --git a/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/JsonRoute.java b/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/JsonRoute.java index 1529dfdeb..f1e324403 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/JsonRoute.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/JsonRoute.java @@ -3,6 +3,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; + /** * @author Daniel Espendiller */ @@ -17,6 +20,9 @@ public class JsonRoute implements RouteInterface { @Nullable private String path; + @Nullable + private Collection methods; + public JsonRoute(@NotNull String name) { this.name = name; } @@ -39,6 +45,12 @@ public String getPath() { return this.path; } + @NotNull + @Override + public Collection getMethods() { + return this.methods == null ? Collections.emptyList() : this.methods; + } + public JsonRoute setPath(@Nullable String path) { this.path = path; @@ -50,4 +62,10 @@ public JsonRoute setController(@Nullable String controller) { return this; } + + public JsonRoute setMethods(@Nullable Collection methods) { + this.methods = methods; + + return this; + } } diff --git a/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/RouteInterface.java b/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/RouteInterface.java index 9bf58e352..b22eeb9a7 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/RouteInterface.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/routing/dict/RouteInterface.java @@ -3,6 +3,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + /** * @author Daniel Espendiller */ @@ -16,4 +18,7 @@ public interface RouteInterface { @Nullable String getPath(); + + @NotNull + Collection getMethods(); } diff --git a/src/fr/adrienbrault/idea/symfony2plugin/stubs/dict/StubIndexedRoute.java b/src/fr/adrienbrault/idea/symfony2plugin/stubs/dict/StubIndexedRoute.java index 53556698b..8c08bd308 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/stubs/dict/StubIndexedRoute.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/stubs/dict/StubIndexedRoute.java @@ -1,13 +1,15 @@ package fr.adrienbrault.idea.symfony2plugin.stubs.dict; +import fr.adrienbrault.idea.symfony2plugin.routing.dict.RouteInterface; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; -public class StubIndexedRoute { +public class StubIndexedRoute implements RouteInterface, Serializable{ @NotNull private final String name; diff --git a/src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/RoutesStubIndex.java b/src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/RoutesStubIndex.java index b38c91fe3..fcbc1d66b 100644 --- a/src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/RoutesStubIndex.java +++ b/src/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/RoutesStubIndex.java @@ -12,7 +12,7 @@ import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper; import fr.adrienbrault.idea.symfony2plugin.stubs.dict.StubIndexedRoute; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.externalizer.ArrayDataExternalizer; +import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.externalizer.ObjectStreamDataExternalizer; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.yaml.YAMLFileType; @@ -21,58 +21,54 @@ import java.util.Map; -public class RoutesStubIndex extends FileBasedIndexExtension { +public class RoutesStubIndex extends FileBasedIndexExtension { - public static final ID KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.routes"); + public static final ID KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.routes_object"); private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); + private static ObjectStreamDataExternalizer EXTERNALIZER = new ObjectStreamDataExternalizer<>(); @NotNull @Override - public ID getName() { + public ID getName() { return KEY; } @NotNull @Override - public DataIndexer getIndexer() { - return new DataIndexer() { - @NotNull - @Override - public Map map(@NotNull FileContent inputData) { - Map map = new THashMap(); - - PsiFile psiFile = inputData.getPsiFile(); - if(!Symfony2ProjectComponent.isEnabledForIndex(psiFile.getProject())) { - return map; - } - - if(psiFile instanceof YAMLFile) { + public DataIndexer getIndexer() { + return inputData -> { + Map map = new THashMap<>(); - if(!isValidForIndex(inputData, psiFile)) { - return map; - } + PsiFile psiFile = inputData.getPsiFile(); + if(!Symfony2ProjectComponent.isEnabledForIndex(psiFile.getProject())) { + return map; + } - YAMLDocument yamlDocument = PsiTreeUtil.getChildOfType(psiFile, YAMLDocument.class); - if(yamlDocument == null) { - return map; - } + if(psiFile instanceof YAMLFile) { - for(StubIndexedRoute indexedRoutes: RouteHelper.getYamlRouteDefinitions(yamlDocument)) { - map.put(indexedRoutes.getName(), new String[] { indexedRoutes.getController(), indexedRoutes.getPath()} ); - } + if(!isValidForIndex(inputData, psiFile)) { + return map; + } + YAMLDocument yamlDocument = PsiTreeUtil.getChildOfType(psiFile, YAMLDocument.class); + if(yamlDocument == null) { return map; } - if(psiFile instanceof XmlFile) { - for(StubIndexedRoute indexedRoutes: RouteHelper.getXmlRouteDefinitions((XmlFile) psiFile)) { - map.put(indexedRoutes.getName(), new String[] { indexedRoutes.getController(), indexedRoutes.getPath()} ); - } + for(StubIndexedRoute indexedRoutes: RouteHelper.getYamlRouteDefinitions(yamlDocument)) { + map.put(indexedRoutes.getName(), indexedRoutes); } return map; } + if(psiFile instanceof XmlFile) { + for(StubIndexedRoute indexedRoutes: RouteHelper.getXmlRouteDefinitions((XmlFile) psiFile)) { + map.put(indexedRoutes.getName(), indexedRoutes); + } + } + + return map; }; } @@ -85,8 +81,8 @@ public KeyDescriptor getKeyDescriptor() { @NotNull @Override - public DataExternalizer getValueExternalizer() { - return new ArrayDataExternalizer(); + public DataExternalizer getValueExternalizer() { + return EXTERNALIZER; } @NotNull @@ -103,7 +99,7 @@ public boolean dependsOnFileContent() { @Override public int getVersion() { - return 1; + return 2; } public static boolean isValidForIndex(FileContent inputData, PsiFile psiFile) { diff --git a/tests/fr/adrienbrault/idea/symfony2plugin/tests/routing/RouteTest.java b/tests/fr/adrienbrault/idea/symfony2plugin/tests/routing/RouteTest.java index efe9e9d53..d17c44440 100644 --- a/tests/fr/adrienbrault/idea/symfony2plugin/tests/routing/RouteTest.java +++ b/tests/fr/adrienbrault/idea/symfony2plugin/tests/routing/RouteTest.java @@ -1,6 +1,7 @@ package fr.adrienbrault.idea.symfony2plugin.tests.routing; import fr.adrienbrault.idea.symfony2plugin.routing.Route; +import fr.adrienbrault.idea.symfony2plugin.stubs.dict.StubIndexedRoute; import org.junit.Assert; import org.junit.Test; @@ -12,29 +13,19 @@ public class RouteTest extends Assert { @Test public void testIndexInit() { - Route route = new Route("foo", new String[] {"foo", "foo_1"}); - assertEquals("foo", route.getController()); - assertEquals("foo_1", route.getPath()); - - route = new Route("foo", new String[] {null, "foo_1"}); - assertEquals("foo_1", route.getPath()); + StubIndexedRoute stubIndexedRoute = new StubIndexedRoute("foo"); + stubIndexedRoute.setPath("foo_1"); + stubIndexedRoute.setController("foo"); - route = new Route("foo", new String[] {"foo"}); + Route route = new Route(stubIndexedRoute); assertEquals("foo", route.getController()); + assertEquals("foo_1", route.getPath()); } @Test public void testIndexNullable() { - Route route = new Route("foo", new String[] {null, null}); - assertNull(route.getController()); - assertNull(route.getPath()); - - route = new Route("foo", new String[] {"", ""}); - assertNull(route.getController()); - assertNull(route.getPath()); - - route = new Route("foo", new String[] {}); - assertEquals("foo", route.getName()); + StubIndexedRoute stubIndexedRoute = new StubIndexedRoute("foo"); + Route route = new Route(stubIndexedRoute); assertNull(route.getController()); assertNull(route.getPath()); } diff --git a/tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTestTest.java b/tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTest.java similarity index 63% rename from tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTestTest.java rename to tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTest.java index 10a0466cf..0a16942e3 100644 --- a/tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTestTest.java +++ b/tests/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/RoutesStubIndexTest.java @@ -9,7 +9,7 @@ * @author Daniel Espendiller * @see fr.adrienbrault.idea.symfony2plugin.stubs.indexes.RoutesStubIndex */ -public class RoutesStubIndexTestTest extends SymfonyLightCodeInsightFixtureTestCase { +public class RoutesStubIndexTest extends SymfonyLightCodeInsightFixtureTestCase { public void setUp() throws Exception { super.setUp(); @@ -32,7 +32,7 @@ public void setUp() throws Exception { myFixture.configureByText(XmlFileType.INSTANCE, "" + "\n" + - " \n" + + " \n" + " \n" + " Foo\n" + " \n" + @@ -54,4 +54,26 @@ public void testRouteIdIndex() { "foo_yaml_invalid" ); } + + /** + * @see fr.adrienbrault.idea.symfony2plugin.stubs.indexes.RoutesStubIndex#getIndexer() + */ + public void testRouteValueIndex() { + assertIndexContainsKeyWithValue(RoutesStubIndex.KEY, "foo_yaml_path", + value -> "foo_controller".equalsIgnoreCase(value.getController()) + ); + } + + /** + * @see fr.adrienbrault.idea.symfony2plugin.stubs.indexes.RoutesStubIndex#getIndexer() + */ + public void testRouteValueWithMethodsInIndex() { + assertIndexContainsKeyWithValue(RoutesStubIndex.KEY, "foo_yaml_pattern", + value -> "foo_yaml_pattern".equalsIgnoreCase(value.getName()) && value.getMethods().contains("get") && value.getMethods().contains("post") + ); + + assertIndexContainsKeyWithValue(RoutesStubIndex.KEY, "foo_xml_pattern", + value -> "foo_xml_pattern".equalsIgnoreCase(value.getName()) && value.getMethods().contains("get") && value.getMethods().contains("post") + ); + } }