From cb1a2b99472faabba3596d673dbdbc377fb1628a Mon Sep 17 00:00:00 2001 From: Peter Lehto Date: Tue, 13 Jun 2017 12:41:36 +0300 Subject: [PATCH] Add getStateParameterMap to Navigator to get parameters as a map (#9517) --- .../java/com/vaadin/navigator/Navigator.java | 87 ++++++++++-- .../tests/server/navigator/NavigatorTest.java | 127 +++++++++++++++--- 2 files changed, 188 insertions(+), 26 deletions(-) diff --git a/server/src/main/java/com/vaadin/navigator/Navigator.java b/server/src/main/java/com/vaadin/navigator/Navigator.java index 2341479832..8b9e39b354 100644 --- a/server/src/main/java/com/vaadin/navigator/Navigator.java +++ b/server/src/main/java/com/vaadin/navigator/Navigator.java @@ -17,9 +17,13 @@ package com.vaadin.navigator; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Objects; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; @@ -53,6 +57,11 @@ public class Navigator implements Serializable { // TODO investigate relationship with TouchKit navigation support + private static final String DEFAULT_VIEW_SEPARATOR = "/"; + + private static final String DEFAULT_STATE_PARAMETER_SEPARATOR = "&"; + private static final String DEFAULT_STATE_PARAMETER_KEY_VALUE_SEPARATOR = "="; + /** * Empty view component. */ @@ -340,23 +349,17 @@ public class Navigator implements Serializable { /** * The {@link UI} bound with the Navigator. - * - * @since 8.0.3 */ protected UI ui; /** * The {@link NavigationStateManager} that is used to get, listen to and * manipulate the navigation state used by the Navigator. - * - * @since 8.0.3 */ protected NavigationStateManager stateManager; /** * The {@link ViewDisplay} used by the Navigator. - * - * @since 8.0.3 */ protected ViewDisplay display; @@ -741,6 +744,76 @@ public class Navigator implements Serializable { return getStateManager().getState(); } + /** + * Returns the current navigation state reported by this Navigator's + * {@link NavigationStateManager} as Map where each key + * represents a parameter in the state. + * + * Uses {@literal &} as parameter separator. If the state contains + * {@literal #!view/foo&bar=baz} then this method will return a map + * containing {@literal foo => ""} and {@literal bar => baz}. + * + * @return The parameters from the navigation state as a map + * @see #getStateParameterMap(String) + */ + public Map getStateParameterMap() { + return getStateParameterMap(DEFAULT_STATE_PARAMETER_SEPARATOR); + } + + /** + * Returns the current navigation state reported by this Navigator's + * {@link NavigationStateManager} as Map where each key + * represents a parameter in the state. The state parameter separator + * character needs to be specified with the separator. + * + * @param separator + * the string (typically one character) used to separate values + * from each other + * @return The parameters from the navigation state as a map + * @see #getStateParameterMap() + */ + public Map getStateParameterMap(String separator) { + return parseStateParameterMap(Objects.requireNonNull(separator)); + } + + /** + * Parses the state parameter map using given separator String. + * + * @param separator + * the string (typically one character) used to separate values + * from each other + * @return The navigation state as Map. + */ + protected Map parseStateParameterMap(String separator) { + Map parameterMap = new HashMap<>(); + if (getState() == null || getState().isEmpty()) { + return Collections.emptyMap(); + } + + String state = getState(); + int viewSeparatorLocation = state.indexOf(DEFAULT_VIEW_SEPARATOR); + + String parameterString; + if (viewSeparatorLocation == -1) { + parameterString = ""; + } else { + parameterString = state.substring(viewSeparatorLocation + 1, + state.length()); + } + if (parameterString.isEmpty()) { + return Collections.emptyMap(); + } + String[] parameters = parameterString.split(separator); + for (int i = 0; i < parameters.length; i++) { + String[] keyAndValue = parameters[i] + .split(DEFAULT_STATE_PARAMETER_KEY_VALUE_SEPARATOR); + parameterMap.put(keyAndValue[0], + keyAndValue.length > 1 ? keyAndValue[1] : ""); + } + + return parameterMap; + } + /** * Return the {@link ViewDisplay} used by the navigator. * @@ -993,8 +1066,6 @@ public class Navigator implements Serializable { * @param state * state string * @return suitable provider - * - * @since 8.0.3 */ protected ViewProvider getViewProvider(String state) { String longestViewName = null; diff --git a/server/src/test/java/com/vaadin/tests/server/navigator/NavigatorTest.java b/server/src/test/java/com/vaadin/tests/server/navigator/NavigatorTest.java index 6991fd1b9e..022b388840 100644 --- a/server/src/test/java/com/vaadin/tests/server/navigator/NavigatorTest.java +++ b/server/src/test/java/com/vaadin/tests/server/navigator/NavigatorTest.java @@ -23,6 +23,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; import org.easymock.EasyMock; import org.easymock.IArgumentMatcher; @@ -51,6 +53,25 @@ import com.vaadin.ui.VerticalLayout; public class NavigatorTest { + private final class TestNavigationStateManager + implements NavigationStateManager { + private String state; + + @Override + public void setState(String state) { + this.state = state; + } + + @Override + public void setNavigator(Navigator navigator) { + } + + @Override + public String getState() { + return state; + } + } + // TODO test internal parameters (and absence of them) // TODO test listeners blocking navigation, multiple listeners @@ -854,24 +875,7 @@ public class NavigatorTest { @Test public void testNavigateTo_navigateSameUriTwice_secondNavigationDoesNothing() { - NavigationStateManager manager = new NavigationStateManager() { - - private String state; - - @Override - public void setState(String state) { - this.state = state; - } - - @Override - public void setNavigator(Navigator navigator) { - } - - @Override - public String getState() { - return state; - } - }; + NavigationStateManager manager = new TestNavigationStateManager(); final String viewName = "view"; @@ -969,4 +973,91 @@ public class NavigatorTest { ((Label) ((HorizontalLayout) ui.getContent()).getComponent(0)) .getValue()); } + + @Test + public void parameterMap_noViewSeparator() { + Navigator navigator = createNavigatorWithState("fooview"); + Assert.assertTrue(navigator.getStateParameterMap().isEmpty()); + Assert.assertTrue(navigator.getStateParameterMap("foo").isEmpty()); + } + + @Test + public void parameterMap_noParameters() { + Navigator navigator = createNavigatorWithState("fooview/"); + Assert.assertTrue(navigator.getStateParameterMap().isEmpty()); + } + + @Test + public void parameterMap_oneParameterNoValue() { + Navigator navigator = createNavigatorWithState("fooview/bar"); + assertMap(navigator.getStateParameterMap(), entry("bar", "")); + } + + @Test + public void parameterMap_oneParameterNoValueButEquals() { + Navigator navigator = createNavigatorWithState("fooview/bar="); + assertMap(navigator.getStateParameterMap(), entry("bar", "")); + } + + @Test + public void parameterMap_oneParameterWithValue() { + Navigator navigator = createNavigatorWithState("fooview/bar=baz"); + assertMap(navigator.getStateParameterMap(), entry("bar", "baz")); + } + + @Test + public void parameterMap_twoParameters() { + Navigator navigator = createNavigatorWithState("fooview/foo=bar&baz"); + assertMap(navigator.getStateParameterMap(), entry("foo", "bar"), + entry("baz", "")); + } + + @Test + public void parameterMap_customSeparator() { + Navigator navigator = createNavigatorWithState("fooview/foo=bar&baz"); + assertMap(navigator.getStateParameterMap("a"), entry("foo", "b"), + entry("r&b", ""), entry("z", "")); + } + + @SafeVarargs + private final void assertMap(Map map, + Entry... entries) { + Assert.assertEquals(entries.length, map.size()); + for (Entry entry : entries) { + Assert.assertTrue( + "Map should contain a key called '" + entry.getKey() + "'", + map.containsKey(entry.getKey())); + Assert.assertEquals(entry.getValue(), map.get(entry.getKey())); + } + + } + + private Entry entry(String key, String value) { + return new Entry() { + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + + }; + } + + private Navigator createNavigatorWithState(String state) { + TestNavigationStateManager manager = new TestNavigationStateManager(); + Navigator navigator = new Navigator(createMockUI(), manager, + EasyMock.createMock(ViewDisplay.class)); + manager.setState(state); + return navigator; + } } -- 2.39.5