@@ -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<String, String> 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<String, String> getStateParameterMap() { | |||
return getStateParameterMap(DEFAULT_STATE_PARAMETER_SEPARATOR); | |||
} | |||
/** | |||
* Returns the current navigation state reported by this Navigator's | |||
* {@link NavigationStateManager} as Map<String, String> 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<String, String> 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<String, String>. | |||
*/ | |||
protected Map<String, String> parseStateParameterMap(String separator) { | |||
Map<String, String> 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; |
@@ -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<String, String> map, | |||
Entry<String, String>... entries) { | |||
Assert.assertEquals(entries.length, map.size()); | |||
for (Entry<String, String> 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<String, String> entry(String key, String value) { | |||
return new Entry<String, String>() { | |||
@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; | |||
} | |||
} |