From 5eed1ac05390e47082c5d9defb55adc9c5b5e88d Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Sun, 16 Nov 2014 15:41:33 +0200 Subject: [PATCH] Provide a way to disallow navigation to the same state twice (#12107). Change-Id: I10e2f2c63402f434ca038d380372a552996102b6 --- .../src/com/vaadin/navigator/Navigator.java | 55 ++++++++--- .../tests/server/navigator/NavigatorTest.java | 93 ++++++++++++++++--- 2 files changed, 122 insertions(+), 26 deletions(-) diff --git a/server/src/com/vaadin/navigator/Navigator.java b/server/src/com/vaadin/navigator/Navigator.java index 82e8de920a..390e252906 100644 --- a/server/src/com/vaadin/navigator/Navigator.java +++ b/server/src/com/vaadin/navigator/Navigator.java @@ -41,6 +41,7 @@ import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; import com.vaadin.server.Page.UriFragmentChangedEvent; import com.vaadin.server.Page.UriFragmentChangedListener; +import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Component; import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.CssLayout; @@ -543,18 +544,11 @@ public class Navigator implements Serializable { * and no error view is registered */ public void navigateTo(String navigationState) { - String longestViewName = null; - ViewProvider longestViewNameProvider = null; + ViewProvider longestViewNameProvider = getViewProvider(navigationState); + String longestViewName = longestViewNameProvider == null ? null + : longestViewNameProvider.getViewName(navigationState); View viewWithLongestName = null; - for (ViewProvider provider : providers) { - String viewName = provider.getViewName(navigationState); - if (null != viewName - && (longestViewName == null || viewName.length() > longestViewName - .length())) { - longestViewName = viewName; - longestViewNameProvider = provider; - } - } + if (longestViewName != null) { viewWithLongestName = longestViewNameProvider .getView(longestViewName); @@ -570,7 +564,15 @@ public class Navigator implements Serializable { parameters = navigationState .substring(longestViewName.length() + 1); } - navigateTo(viewWithLongestName, longestViewName, parameters); + if (getCurrentView() == null + || !SharedUtil + .equals(getCurrentView(), viewWithLongestName)) { + navigateTo(viewWithLongestName, longestViewName, parameters); + } else { + updateNavigationState(new ViewChangeEvent(this, + getCurrentView(), viewWithLongestName, longestViewName, + parameters)); + } } else { throw new IllegalArgumentException( "Trying to navigate to an unknown state '" @@ -986,8 +988,33 @@ public class Navigator implements Serializable { } /** - * Destroys the navigator and cleans it up. The method detaches the - * navigator from UI and removes all view change listeners. + * Get view provider that handles the given {@code state}. + * + * @param state + * state string + * @return suitable provider + */ + private ViewProvider getViewProvider(String state) { + String longestViewName = null; + ViewProvider longestViewNameProvider = null; + for (ViewProvider provider : providers) { + String viewName = provider.getViewName(state); + if (null != viewName + && (longestViewName == null || viewName.length() > longestViewName + .length())) { + longestViewName = viewName; + longestViewNameProvider = provider; + } + } + return longestViewNameProvider; + } + + /** + * Creates view change event for given {@code view}, {@code viewName} and + * {@code parameters}. + * + * @since + * @return view change event */ public void destroy() { stateManager.setNavigator(null); diff --git a/server/tests/src/com/vaadin/tests/server/navigator/NavigatorTest.java b/server/tests/src/com/vaadin/tests/server/navigator/NavigatorTest.java index 0de804fa0b..a490202d68 100644 --- a/server/tests/src/com/vaadin/tests/server/navigator/NavigatorTest.java +++ b/server/tests/src/com/vaadin/tests/server/navigator/NavigatorTest.java @@ -24,6 +24,7 @@ import org.easymock.EasyMock; import org.easymock.IArgumentMatcher; import org.easymock.IMocksControl; import org.junit.Assert; +import org.junit.Test; import com.vaadin.navigator.NavigationStateManager; import com.vaadin.navigator.Navigator; @@ -314,7 +315,8 @@ public class NavigatorTest extends TestCase { // prepare mocks: what to expect manager.setNavigator(EasyMock.anyObject(Navigator.class)); - EasyMock.expect(provider.getViewName("test1")).andReturn("test1"); + EasyMock.expect(provider.getViewName("test1")).andReturn("test1") + .times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn(""); view1.enter(eventParametersEqual("")); @@ -322,7 +324,8 @@ public class NavigatorTest extends TestCase { manager.setState("test1"); EasyMock.expect(manager.getState()).andReturn("test1"); - EasyMock.expect(provider.getViewName("test2/")).andReturn("test2"); + EasyMock.expect(provider.getViewName("test2/")).andReturn("test2") + .times(2); EasyMock.expect(provider.getView("test2")).andReturn(view2); EasyMock.expect(manager.getState()).andReturn("test1"); view2.enter(eventParametersEqual("")); @@ -331,7 +334,7 @@ public class NavigatorTest extends TestCase { EasyMock.expect(manager.getState()).andReturn("test2"); EasyMock.expect(provider.getViewName("test1/params")) - .andReturn("test1"); + .andReturn("test1").times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn("test2"); view1.enter(eventParametersEqual("params")); @@ -367,14 +370,15 @@ public class NavigatorTest extends TestCase { // prepare mocks: what to expect manager.setNavigator(EasyMock.anyObject(Navigator.class)); - EasyMock.expect(provider.getViewName("test2")).andReturn("test2"); + EasyMock.expect(provider.getViewName("test2")).andReturn("test2") + .times(2); EasyMock.expect(provider.getView("test2")).andReturn(view2); EasyMock.expect(manager.getState()).andReturn("view1"); view2.enter(eventParametersEqual("")); display.showView(view2); manager.setState("test2"); - EasyMock.expect(provider.getViewName("")).andReturn("test1"); + EasyMock.expect(provider.getViewName("")).andReturn("test1").times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn(""); view1.enter(eventParametersEqual("")); @@ -382,7 +386,7 @@ public class NavigatorTest extends TestCase { manager.setState("test1"); EasyMock.expect(provider.getViewName("test1/params")) - .andReturn("test1"); + .andReturn("test1").times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn("test2"); view1.enter(eventParametersEqual("params")); @@ -414,7 +418,8 @@ public class NavigatorTest extends TestCase { Navigator navigator = createNavigator(manager, display); // prepare mocks: what to expect - EasyMock.expect(provider.getViewName("test1")).andReturn("test1"); + EasyMock.expect(provider.getViewName("test1")).andReturn("test1") + .times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); ViewChangeEvent event1 = new ViewChangeEvent(navigator, null, view1, "test1", ""); @@ -425,7 +430,8 @@ public class NavigatorTest extends TestCase { manager.setState("test1"); listener.addExpectedNavigatorViewChange(event1); - EasyMock.expect(provider.getViewName("test2")).andReturn("test2"); + EasyMock.expect(provider.getViewName("test2")).andReturn("test2") + .times(2); EasyMock.expect(provider.getView("test2")).andReturn(view2); ViewChangeEvent event2 = new ViewChangeEvent(navigator, view1, view2, "test2", ""); @@ -493,7 +499,8 @@ public class NavigatorTest extends TestCase { // prepare mocks: what to expect // first listener blocks first view change - EasyMock.expect(provider.getViewName("test1")).andReturn("test1"); + EasyMock.expect(provider.getViewName("test1")).andReturn("test1") + .times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn(""); ViewChangeEvent event1 = new ViewChangeEvent(navigator, null, view1, @@ -501,7 +508,8 @@ public class NavigatorTest extends TestCase { listener1.addExpectedIsViewChangeAllowed(event1, false); // second listener blocks second view change - EasyMock.expect(provider.getViewName("test1/test")).andReturn("test1"); + EasyMock.expect(provider.getViewName("test1/test")).andReturn("test1") + .times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn(""); ViewChangeEvent event2 = new ViewChangeEvent(navigator, null, view1, @@ -510,7 +518,8 @@ public class NavigatorTest extends TestCase { listener2.addExpectedIsViewChangeAllowed(event2, false); // both listeners allow view change - EasyMock.expect(provider.getViewName("test1/bar")).andReturn("test1"); + EasyMock.expect(provider.getViewName("test1/bar")).andReturn("test1") + .times(2); EasyMock.expect(provider.getView("test1")).andReturn(view1); EasyMock.expect(manager.getState()).andReturn(""); ViewChangeEvent event3 = new ViewChangeEvent(navigator, null, view1, @@ -524,7 +533,8 @@ public class NavigatorTest extends TestCase { listener2.addExpectedNavigatorViewChange(event3); // both listeners allow view change from non-null view - EasyMock.expect(provider.getViewName("test2")).andReturn("test2"); + EasyMock.expect(provider.getViewName("test2")).andReturn("test2") + .times(2); EasyMock.expect(provider.getView("test2")).andReturn(view2); EasyMock.expect(manager.getState()).andReturn("view1"); ViewChangeEvent event4 = new ViewChangeEvent(navigator, view1, view2, @@ -821,4 +831,63 @@ public class NavigatorTest extends TestCase { // Expected } } + + @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; + } + }; + + final String viewName = "view"; + + final View view = EasyMock.createMock(View.class); + ViewProvider provider = new ViewProvider() { + + @Override + public String getViewName(String viewAndParameters) { + return viewName; + } + + @Override + public View getView(String viewName) { + return view; + } + + }; + + final int[] count = new int[] { 0 }; + Navigator navigator = new Navigator(createMockUI(), manager, + EasyMock.createMock(ViewDisplay.class)) { + @Override + protected void navigateTo(View view, String viewName, + String parameters) { + count[0] = count[0] + 1; + super.navigateTo(view, viewName, parameters); + } + }; + navigator.addProvider(provider); + + // First time navigation + navigator.navigateTo(viewName); + Assert.assertEquals(1, count[0]); + + // Second time navigation to the same view + navigator.navigateTo(viewName); + Assert.assertEquals(1, count[0]); + } } -- 2.39.5