From 7c94e50da6cd0c723811170ceb66bccef662e123 Mon Sep 17 00:00:00 2001 From: Avinash Singhal Date: Thu, 9 Aug 2012 16:02:53 +0530 Subject: [PATCH 1/2] Fixed BreadCrumbs Separator UI Issue Earlier it was showing >> First View >> Second View, now it will show First View >> Second View Changes addSeparatorForView method. --- .../mvp4vaadin/navigation/ui/Breadcrumbs.java | 586 +++++++++--------- 1 file changed, 293 insertions(+), 293 deletions(-) diff --git a/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/ui/Breadcrumbs.java b/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/ui/Breadcrumbs.java index 36df074..56dfebb 100644 --- a/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/ui/Breadcrumbs.java +++ b/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/ui/Breadcrumbs.java @@ -1,293 +1,293 @@ -/* - * Copyright (c) 2011 Petter Holmström - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.peholmst.mvp4vaadin.navigation.ui; - -import java.util.HashMap; -import java.util.Map; - -import com.github.peholmst.mvp4vaadin.View; -import com.github.peholmst.mvp4vaadin.ViewEvent; -import com.github.peholmst.mvp4vaadin.ViewListener; -import com.github.peholmst.mvp4vaadin.events.DescriptionChangedViewEvent; -import com.github.peholmst.mvp4vaadin.events.DisplayNameChangedViewEvent; -import com.github.peholmst.mvp4vaadin.navigation.NavigationController; -import com.github.peholmst.mvp4vaadin.navigation.NavigationControllerEvent; -import com.github.peholmst.mvp4vaadin.navigation.NavigationControllerListener; -import com.github.peholmst.mvp4vaadin.navigation.NavigationRequest; -import com.github.peholmst.mvp4vaadin.navigation.NavigationRequestBuilder; -import com.github.peholmst.mvp4vaadin.navigation.events.CurrentNavigationControllerViewChangedEvent; -import com.vaadin.ui.Alignment; -import com.vaadin.ui.Button; -import com.vaadin.ui.Component; -import com.vaadin.ui.HorizontalLayout; -import com.vaadin.ui.Label; -import com.vaadin.ui.Button.ClickEvent; -import com.vaadin.ui.themes.BaseTheme; - -/** - * This class implements a breadcrumb navigation bar that shows all the views - * currently in a {@link NavigationController} as links in a row, where the - * first view corresponds to the first link, etc: - * - *
- * First view >> Second view >> Third view >> ...
- * 
- *

- * As the views change in the controller, the navigation bar will update itself. - * A click on any of the navigation links will request the view controller to - * navigate to that particular view. - *

- * Both the links and the separators can be customized by implementing the - * {@link ButtonFactory} and {@link SeparatorFactory} interfaces, respectively. - * - * @see #setController(NavigationController) - * - * @author Petter Holmström - * @since 1.0 - */ -public class Breadcrumbs extends HorizontalLayout implements - NavigationControllerListener, ViewListener { - - private static final long serialVersionUID = 4513495936876605206L; - - public static final String BREADCRUMB_ELEMENT = "breadcrumb-element"; - - /** - * Factory interface for creating breadcrumb separators. - * - * @see Breadcrumbs#setSeparatorFactory(SeparatorFactory) - * @author Petter Holmström - * @since 1.0 - */ - public static interface SeparatorFactory extends java.io.Serializable { - /** - * Creates and returns a component to be used as a separator between - * breadcrumbs. - */ - Component createSeparator(); - } - - /** - * Default implementation of {@link SeparatorFactory}. The separators are - * labels containing the "»" character and having the - * {@link Breadcrumbs#BREADCRUMB_ELEMENT} style. - * - * @author Petter Holmström - * @since 1.0 - */ - public static class DefaultSeparatorFactory implements SeparatorFactory { - - private static final long serialVersionUID = 7957216244739746986L; - - @Override - public Component createSeparator() { - final Label separator = new Label("»"); - separator.setSizeUndefined(); - separator.addStyleName(BREADCRUMB_ELEMENT); - return separator; - } - } - - /** - * Factory interface for creating breadcrumb buttons. - * - * @see Breadcrumbs#setButtonFactory(ButtonFactory) - * @author Petter Holmström - * @since 1.0 - */ - public static interface ButtonFactory extends java.io.Serializable { - - /** - * Creates and returns a button for the specified view. The click - * listener will be registered by the breadcrumbs component. - */ - Button createButton(View view); - - /** - * Updates the button texts. This method is called when the display name - * and/or the description of the specified view are changed. - */ - void updateButtonTexts(Button button, View view); - } - - /** - * Default implementation of {@link ButtonFactory}. The created buttons have - * the {@link BaseTheme#BUTTON_LINK} and - * {@link Breadcrumbs#BREADCRUMB_ELEMENT} styles. - * - * @author Petter Holmström - * @since 1.0 - */ - public static class DefaultButtonFactory implements ButtonFactory { - - private static final long serialVersionUID = 8031407455065485896L; - - @Override - public Button createButton(View view) { - final Button btn = new Button(); - btn.setStyleName(BaseTheme.BUTTON_LINK); - btn.setSizeUndefined(); - btn.addStyleName(BREADCRUMB_ELEMENT); - updateButtonTexts(btn, view); - return btn; - } - - @Override - public void updateButtonTexts(Button button, View view) { - button.setCaption(view.getDisplayName()); - button.setDescription(view.getViewDescription()); - } - } - - private SeparatorFactory separatorFactory = new DefaultSeparatorFactory(); - - private ButtonFactory buttonFactory = new DefaultButtonFactory(); - - private NavigationController controller; - - private Map viewButtonMap = new HashMap(); - - /** - * Returns the navigation controller whose view stack will be displayed as - * breadcrumbs. If no controller has been set, null is - * returned. - */ - public NavigationController getController() { - return controller; - } - - /** - * Sets the navigation controller to use. This component will register - * itself as a listener of the controller. Setting the controller to - * null will unregister the listener. - */ - public void setController(NavigationController controller) { - if (this.controller != null) { - this.controller.removeListener(this); - } - this.controller = controller; - addBreadcrumbsForControllerRemovingAnyExistingOnes(); - if (this.controller != null) { - this.controller.addListener(this); - } - } - - /** - * Returns the separator factory to use for creating separators between - * breadcrumb buttons. - */ - public SeparatorFactory getSeparatorFactory() { - return separatorFactory; - } - - /** - * Sets the separator factory to use for creating separators between - * breadcrumb buttons. Set this value to null to use the - * default separator factory. - */ - public void setSeparatorFactory(SeparatorFactory separatorFactory) { - if (separatorFactory == null) { - separatorFactory = new DefaultSeparatorFactory(); - } - this.separatorFactory = separatorFactory; - } - - /** - * Returns the button factory to use for creating breadcrumb buttons. - */ - public ButtonFactory getButtonFactory() { - return buttonFactory; - } - - /** - * Sets the button factory to use for creating breadcrumb buttons. Set this - * value to null to use the default button factory. - */ - public void setButtonFactory(ButtonFactory buttonFactory) { - if (buttonFactory == null) { - buttonFactory = new DefaultButtonFactory(); - } - this.buttonFactory = buttonFactory; - } - - private void addBreadcrumbsForControllerRemovingAnyExistingOnes() { - removeBreadcrumbs(); - if (getController() != null) { - for (View view : getController().getViewStack()) { - addBreadcrumbForView(view); - } - } - } - - @Override - public void handleNavigationControllerEvent(NavigationControllerEvent event) { - if (event.getSource() != getController() - || !(event instanceof CurrentNavigationControllerViewChangedEvent)) { - return; - } - // TODO Maybe this method should be optimized so that not all buttons - // need to be removed unless absolutely necessary. - addBreadcrumbsForControllerRemovingAnyExistingOnes(); - } - - protected void addBreadcrumbForView(final View view) { - addSeparatorForView(view); - final Button btn = getButtonFactory().createButton(view); - final NavigationRequest navigationRequest = NavigationRequestBuilder - .newInstance().startWithPathToView(getController(), view) - .buildRequest(); - btn.addListener(new Button.ClickListener() { - - private static final long serialVersionUID = 5199653237630939848L; - - @Override - public void buttonClick(ClickEvent event) { - getController().navigate(navigationRequest); - } - }); - viewButtonMap.put(view, btn); - view.addListener(this); - addComponent(btn); - setComponentAlignment(btn, Alignment.MIDDLE_LEFT); - } - - protected void addSeparatorForView(final View view) { - if (getController().containsMoreThanOneElement()) { - Component separator = getSeparatorFactory().createSeparator(); - addComponent(separator); - setComponentAlignment(separator, Alignment.MIDDLE_LEFT); - } - } - - protected void removeBreadcrumbs() { - removeAllComponents(); - for (View view : viewButtonMap.keySet()) { - view.removeListener(this); - } - viewButtonMap.clear(); - } - - @Override - public void handleViewEvent(ViewEvent event) { - if (event instanceof DisplayNameChangedViewEvent - || event instanceof DescriptionChangedViewEvent) { - final Button btn = viewButtonMap.get(event.getSource()); - if (btn != null) { - getButtonFactory().updateButtonTexts(btn, event.getSource()); - } - } - } -} +/* + * Copyright (c) 2011 Petter Holmström + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.peholmst.mvp4vaadin.navigation.ui; + +import java.util.HashMap; +import java.util.Map; + +import com.github.peholmst.mvp4vaadin.View; +import com.github.peholmst.mvp4vaadin.ViewEvent; +import com.github.peholmst.mvp4vaadin.ViewListener; +import com.github.peholmst.mvp4vaadin.events.DescriptionChangedViewEvent; +import com.github.peholmst.mvp4vaadin.events.DisplayNameChangedViewEvent; +import com.github.peholmst.mvp4vaadin.navigation.NavigationController; +import com.github.peholmst.mvp4vaadin.navigation.NavigationControllerEvent; +import com.github.peholmst.mvp4vaadin.navigation.NavigationControllerListener; +import com.github.peholmst.mvp4vaadin.navigation.NavigationRequest; +import com.github.peholmst.mvp4vaadin.navigation.NavigationRequestBuilder; +import com.github.peholmst.mvp4vaadin.navigation.events.CurrentNavigationControllerViewChangedEvent; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.themes.BaseTheme; + +/** + * This class implements a breadcrumb navigation bar that shows all the views + * currently in a {@link NavigationController} as links in a row, where the + * first view corresponds to the first link, etc: + * + *

+ * First view >> Second view >> Third view >> ...
+ * 
+ *

+ * As the views change in the controller, the navigation bar will update itself. + * A click on any of the navigation links will request the view controller to + * navigate to that particular view. + *

+ * Both the links and the separators can be customized by implementing the + * {@link ButtonFactory} and {@link SeparatorFactory} interfaces, respectively. + * + * @see #setController(NavigationController) + * + * @author Petter Holmström + * @since 1.0 + */ +public class Breadcrumbs extends HorizontalLayout implements + NavigationControllerListener, ViewListener { + + private static final long serialVersionUID = 4513495936876605206L; + + public static final String BREADCRUMB_ELEMENT = "breadcrumb-element"; + + /** + * Factory interface for creating breadcrumb separators. + * + * @see Breadcrumbs#setSeparatorFactory(SeparatorFactory) + * @author Petter Holmström + * @since 1.0 + */ + public static interface SeparatorFactory extends java.io.Serializable { + /** + * Creates and returns a component to be used as a separator between + * breadcrumbs. + */ + Component createSeparator(); + } + + /** + * Default implementation of {@link SeparatorFactory}. The separators are + * labels containing the "»" character and having the + * {@link Breadcrumbs#BREADCRUMB_ELEMENT} style. + * + * @author Petter Holmström + * @since 1.0 + */ + public static class DefaultSeparatorFactory implements SeparatorFactory { + + private static final long serialVersionUID = 7957216244739746986L; + + @Override + public Component createSeparator() { + final Label separator = new Label("»"); + separator.setSizeUndefined(); + separator.addStyleName(BREADCRUMB_ELEMENT); + return separator; + } + } + + /** + * Factory interface for creating breadcrumb buttons. + * + * @see Breadcrumbs#setButtonFactory(ButtonFactory) + * @author Petter Holmström + * @since 1.0 + */ + public static interface ButtonFactory extends java.io.Serializable { + + /** + * Creates and returns a button for the specified view. The click + * listener will be registered by the breadcrumbs component. + */ + Button createButton(View view); + + /** + * Updates the button texts. This method is called when the display name + * and/or the description of the specified view are changed. + */ + void updateButtonTexts(Button button, View view); + } + + /** + * Default implementation of {@link ButtonFactory}. The created buttons have + * the {@link BaseTheme#BUTTON_LINK} and + * {@link Breadcrumbs#BREADCRUMB_ELEMENT} styles. + * + * @author Petter Holmström + * @since 1.0 + */ + public static class DefaultButtonFactory implements ButtonFactory { + + private static final long serialVersionUID = 8031407455065485896L; + + @Override + public Button createButton(View view) { + final Button btn = new Button(); + btn.setStyleName(BaseTheme.BUTTON_LINK); + btn.setSizeUndefined(); + btn.addStyleName(BREADCRUMB_ELEMENT); + updateButtonTexts(btn, view); + return btn; + } + + @Override + public void updateButtonTexts(Button button, View view) { + button.setCaption(view.getDisplayName()); + button.setDescription(view.getViewDescription()); + } + } + + private SeparatorFactory separatorFactory = new DefaultSeparatorFactory(); + + private ButtonFactory buttonFactory = new DefaultButtonFactory(); + + private NavigationController controller; + + private Map viewButtonMap = new HashMap(); + + /** + * Returns the navigation controller whose view stack will be displayed as + * breadcrumbs. If no controller has been set, null is + * returned. + */ + public NavigationController getController() { + return controller; + } + + /** + * Sets the navigation controller to use. This component will register + * itself as a listener of the controller. Setting the controller to + * null will unregister the listener. + */ + public void setController(NavigationController controller) { + if (this.controller != null) { + this.controller.removeListener(this); + } + this.controller = controller; + addBreadcrumbsForControllerRemovingAnyExistingOnes(); + if (this.controller != null) { + this.controller.addListener(this); + } + } + + /** + * Returns the separator factory to use for creating separators between + * breadcrumb buttons. + */ + public SeparatorFactory getSeparatorFactory() { + return separatorFactory; + } + + /** + * Sets the separator factory to use for creating separators between + * breadcrumb buttons. Set this value to null to use the + * default separator factory. + */ + public void setSeparatorFactory(SeparatorFactory separatorFactory) { + if (separatorFactory == null) { + separatorFactory = new DefaultSeparatorFactory(); + } + this.separatorFactory = separatorFactory; + } + + /** + * Returns the button factory to use for creating breadcrumb buttons. + */ + public ButtonFactory getButtonFactory() { + return buttonFactory; + } + + /** + * Sets the button factory to use for creating breadcrumb buttons. Set this + * value to null to use the default button factory. + */ + public void setButtonFactory(ButtonFactory buttonFactory) { + if (buttonFactory == null) { + buttonFactory = new DefaultButtonFactory(); + } + this.buttonFactory = buttonFactory; + } + + private void addBreadcrumbsForControllerRemovingAnyExistingOnes() { + removeBreadcrumbs(); + if (getController() != null) { + for (View view : getController().getViewStack()) { + addBreadcrumbForView(view); + } + } + } + + @Override + public void handleNavigationControllerEvent(NavigationControllerEvent event) { + if (event.getSource() != getController() + || !(event instanceof CurrentNavigationControllerViewChangedEvent)) { + return; + } + // TODO Maybe this method should be optimized so that not all buttons + // need to be removed unless absolutely necessary. + addBreadcrumbsForControllerRemovingAnyExistingOnes(); + } + + protected void addBreadcrumbForView(final View view) { + addSeparatorForView(view); + final Button btn = getButtonFactory().createButton(view); + final NavigationRequest navigationRequest = NavigationRequestBuilder + .newInstance().startWithPathToView(getController(), view) + .buildRequest(); + btn.addListener(new Button.ClickListener() { + + private static final long serialVersionUID = 5199653237630939848L; + + @Override + public void buttonClick(ClickEvent event) { + getController().navigate(navigationRequest); + } + }); + viewButtonMap.put(view, btn); + view.addListener(this); + addComponent(btn); + setComponentAlignment(btn, Alignment.MIDDLE_LEFT); + } + + protected void addSeparatorForView(final View view) { + if (getController().containsMoreThanOneElement() && !getController().getFirstView().equals(view)) { + Component separator = getSeparatorFactory().createSeparator(); + addComponent(separator); + setComponentAlignment(separator, Alignment.MIDDLE_LEFT); + } + } + + protected void removeBreadcrumbs() { + removeAllComponents(); + for (View view : viewButtonMap.keySet()) { + view.removeListener(this); + } + viewButtonMap.clear(); + } + + @Override + public void handleViewEvent(ViewEvent event) { + if (event instanceof DisplayNameChangedViewEvent + || event instanceof DescriptionChangedViewEvent) { + final Button btn = viewButtonMap.get(event.getSource()); + if (btn != null) { + getButtonFactory().updateButtonTexts(btn, event.getSource()); + } + } + } +} From f43b8c22b093b6eccb7759b7e48c1f203deee106 Mon Sep 17 00:00:00 2001 From: Avinash Singhal Date: Thu, 9 Aug 2012 16:06:09 +0530 Subject: [PATCH 2/2] Fixed Issue in Setting Parameters using Map in setParams function globale and local variable name was same, so changes were not being loaded in global variable. Just added this. in setParams Function to fix it. --- .../navigation/NavigationRequestBuilder.java | 668 +++++++++--------- 1 file changed, 334 insertions(+), 334 deletions(-) diff --git a/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/NavigationRequestBuilder.java b/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/NavigationRequestBuilder.java index 0c437a5..b3fb591 100644 --- a/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/NavigationRequestBuilder.java +++ b/Sources/MVP4Vaadin/src/com/github/peholmst/mvp4vaadin/navigation/NavigationRequestBuilder.java @@ -1,334 +1,334 @@ -/* - * Copyright (c) 2011 Petter Holmström - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.peholmst.mvp4vaadin.navigation; - -import java.lang.reflect.Constructor; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.github.peholmst.mvp4vaadin.View; - -/** - * This class implements a builder for creating new {@link NavigationRequest} - * instances. This is how it is to be used, using the default path builder: - *

- *

- * Other path builders can be plugged in by using the - * {@link #newInstance(Class)} factory method. - * - * @author Petter Holmström - * @since 1.0 - */ -public final class NavigationRequestBuilder

{ - - private final Class

pathBuilderClass; - - private P pathBuilder; - - private final HashMap params = new HashMap(); - - private NavigationRequestBuilder(Class

pathBuilderClass) { - this.pathBuilderClass = pathBuilderClass; - } - - private P createPathBuilder() { - try { - final Constructor

constructor = pathBuilderClass - .getConstructor(NavigationRequestBuilder.class); - return constructor.newInstance(this); - } catch (Exception e) { - throw new RuntimeException("Could not create path builder", e); - } - } - - private P createPathBuilder(List initialPath) { - try { - final Constructor

constructor = pathBuilderClass.getConstructor( - NavigationRequestBuilder.class, List.class); - return constructor.newInstance(this, initialPath); - } catch (Exception e) { - throw new RuntimeException("Could not create path builder", e); - } - } - - /** - * Base class for a path builder that builds the - * {@link NavigationRequest#getPath() path} of a {@link NavigationRequest}. - * - * @author Petter Holmström - * @since 1.0 - */ - public static abstract class PathBuilder { - - private final NavigationRequestBuilder requestBuilder; - private final LinkedList path = new LinkedList(); - - /** - * Creates a new PathBuilder. - * - * @param requestBuilder - * the owning request builder. - */ - public PathBuilder(NavigationRequestBuilder requestBuilder) { - this.requestBuilder = requestBuilder; - } - - /** - * Creates a new PathBuilder. - * - * @param requestBuilder - * the owning request builder. - * @param initialPath - * the initial path. - */ - public PathBuilder(NavigationRequestBuilder requestBuilder, - List initialPath) { - this(requestBuilder); - path.addAll(initialPath); - } - - /** - * Returns the path to which views can be added. - */ - protected final LinkedList getPath() { - return path; - } - - /** - * Builds a {@link NavigationRequest} instance for the current path and - * returns it. - * - * @throws IllegalStateException - * if the path is empty. - */ - @SuppressWarnings("unchecked") - public NavigationRequest buildRequest() throws IllegalStateException { - if (getPath().isEmpty()) { - throw new IllegalStateException( - "The path must contain at least one view"); - } - final List copyOfPath = Collections - .unmodifiableList((List) getPath().clone()); - final Map copyOfParams = Collections - .unmodifiableMap((Map) requestBuilder.params - .clone()); - return new NavigationRequest() { - - private static final long serialVersionUID = -6273102646598049858L; - - @Override - public List getPath() { - return copyOfPath; - } - - @Override - public Map getParams() { - return copyOfParams; - } - }; - } - - } - - /** - * A path builder is used to construct the path of the - * {@link NavigationRequest}. The request itself is built by calling the - * {@link #buildRequest()} method. - * - * @author Petter Holmström - * @since 1.0 - */ - public static final class DefaultPathBuilder extends PathBuilder { - - public DefaultPathBuilder(NavigationRequestBuilder requestBuilder) { - super(requestBuilder); - } - - public DefaultPathBuilder(NavigationRequestBuilder requestBuilder, - List initialPath) { - super(requestBuilder, initialPath); - } - - /** - * Adds the specified view to the path. - */ - public DefaultPathBuilder addViewToPath(View view) { - getPath().add(view); - return this; - } - - /** - * Adds the specified views to the path. - */ - public DefaultPathBuilder addViewsToPath(View... views) { - getPath().addAll(Arrays.asList(views)); - return this; - } - } - - /** - * Sets the value of a single parameter to be passed to the view. - */ - public NavigationRequestBuilder

setParam(String paramName, - Object paramValue) { - params.put(paramName, paramValue); - return this; - } - - /** - * Sets the values of multiple parameters to be passed to the view. - */ - public NavigationRequestBuilder

setParams(Map params) { - params.putAll(params); - return this; - } - - /** - * Returns a {@link PathBuilder} that starts from the previous view (i.e. - * the view behind the current view) of the specified view controller. This - * path can be used to perform a "go back" navigation. - * - * @throws IllegalStateException - * if there are less than two views in the controller's stack, - * or if another path builder has already been created. - */ - public P startWithPathToPreviousView(NavigationController controller) - throws IllegalStateException { - if (controller.getViewStack().size() < 2) { - throw new IllegalStateException( - "Not enough views in controller to start from the previous view"); - } - verifyPathBuilderNotSet(); - pathBuilder = createPathBuilder(controller.getViewStack().subList(0, - controller.getViewStack().size() - 1)); - return pathBuilder; - } - - /** - * Returns a {@link PathBuilder} that starts from the first view of the - * specified view controller. This path can be used to perform a "go home" - * navigation. - * - * @throws IllegalStateException - * if the controller's stack is empty, or if another path - * builder has already been created. - */ - public P startWithPathToFirstView(NavigationController controller) - throws IllegalStateException { - if (controller.isEmpty()) { - throw new IllegalStateException( - "Controller is empty, cannot start from the first view"); - } - verifyPathBuilderNotSet(); - pathBuilder = createPathBuilder(controller.getViewStack().subList(0, 1)); - return pathBuilder; - } - - /** - * Returns a {@link PathBuilder} that starts from the current view of the - * specified view controller. This path can be used when adding a new view - * to the stack. If the controller is empty, this call has the same effect - * as using {@link #startWithEmptyPath()}. - * - * @throws IllegalStateException - * if another path builder has already been created. - */ - public P startWithPathToCurrentView(NavigationController controller) - throws IllegalStateException { - verifyPathBuilderNotSet(); - pathBuilder = createPathBuilder(controller.getViewStack()); - return pathBuilder; - } - - /** - * Returns a {@link PathBuilder} that starts from the path to the specified - * view. - * - * @throws IllegalStateException - * if another path builder has already been created or the - * specified view cannot be found in the controller. - */ - public P startWithPathToView(NavigationController controller, View view) - throws IllegalStateException { - verifyPathBuilderNotSet(); - LinkedList path = new LinkedList(); - boolean found = false; - for (View viewInPath : controller.getViewStack()) { - path.add(viewInPath); - if (viewInPath.equals(view)) { - found = true; - break; - } - } - if (!found) { - throw new IllegalStateException("View not found in controller"); - } - pathBuilder = createPathBuilder(path); - return pathBuilder; - } - - /** - * Returns a {@link PathBuilder} that starts with an empty path. At least - * one view has to be added before the navigation request can be built. - * - * @throws IllegalStateException - * if another path builder has already been created. - */ - public P startWithEmptyPath() throws IllegalStateException { - verifyPathBuilderNotSet(); - pathBuilder = createPathBuilder(); - return pathBuilder; - } - - private void verifyPathBuilderNotSet() throws IllegalStateException { - if (pathBuilder != null) { - throw new IllegalStateException( - "A pathBuilder has already been created"); - } - } - - /** - * Returns a new navigation request builder instance that uses the default - * path builder. - */ - public static NavigationRequestBuilder newInstance() { - return new NavigationRequestBuilder( - DefaultPathBuilder.class); - } - - /** - * Returns a new navigation request builder instance that uses a path - * builder of the specified class. - */ - public static

NavigationRequestBuilder

newInstance( - Class

pathBuilderClass) { - return new NavigationRequestBuilder

(pathBuilderClass); - } -} +/* + * Copyright (c) 2011 Petter Holmström + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.peholmst.mvp4vaadin.navigation; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.github.peholmst.mvp4vaadin.View; + +/** + * This class implements a builder for creating new {@link NavigationRequest} + * instances. This is how it is to be used, using the default path builder: + *

+ *

+ * Other path builders can be plugged in by using the + * {@link #newInstance(Class)} factory method. + * + * @author Petter Holmström + * @since 1.0 + */ +public final class NavigationRequestBuilder

{ + + private final Class

pathBuilderClass; + + private P pathBuilder; + + private final HashMap params = new HashMap(); + + private NavigationRequestBuilder(Class

pathBuilderClass) { + this.pathBuilderClass = pathBuilderClass; + } + + private P createPathBuilder() { + try { + final Constructor

constructor = pathBuilderClass + .getConstructor(NavigationRequestBuilder.class); + return constructor.newInstance(this); + } catch (Exception e) { + throw new RuntimeException("Could not create path builder", e); + } + } + + private P createPathBuilder(List initialPath) { + try { + final Constructor

constructor = pathBuilderClass.getConstructor( + NavigationRequestBuilder.class, List.class); + return constructor.newInstance(this, initialPath); + } catch (Exception e) { + throw new RuntimeException("Could not create path builder", e); + } + } + + /** + * Base class for a path builder that builds the + * {@link NavigationRequest#getPath() path} of a {@link NavigationRequest}. + * + * @author Petter Holmström + * @since 1.0 + */ + public static abstract class PathBuilder { + + private final NavigationRequestBuilder requestBuilder; + private final LinkedList path = new LinkedList(); + + /** + * Creates a new PathBuilder. + * + * @param requestBuilder + * the owning request builder. + */ + public PathBuilder(NavigationRequestBuilder requestBuilder) { + this.requestBuilder = requestBuilder; + } + + /** + * Creates a new PathBuilder. + * + * @param requestBuilder + * the owning request builder. + * @param initialPath + * the initial path. + */ + public PathBuilder(NavigationRequestBuilder requestBuilder, + List initialPath) { + this(requestBuilder); + path.addAll(initialPath); + } + + /** + * Returns the path to which views can be added. + */ + protected final LinkedList getPath() { + return path; + } + + /** + * Builds a {@link NavigationRequest} instance for the current path and + * returns it. + * + * @throws IllegalStateException + * if the path is empty. + */ + @SuppressWarnings("unchecked") + public NavigationRequest buildRequest() throws IllegalStateException { + if (getPath().isEmpty()) { + throw new IllegalStateException( + "The path must contain at least one view"); + } + final List copyOfPath = Collections + .unmodifiableList((List) getPath().clone()); + final Map copyOfParams = Collections + .unmodifiableMap((Map) requestBuilder.params + .clone()); + return new NavigationRequest() { + + private static final long serialVersionUID = -6273102646598049858L; + + @Override + public List getPath() { + return copyOfPath; + } + + @Override + public Map getParams() { + return copyOfParams; + } + }; + } + + } + + /** + * A path builder is used to construct the path of the + * {@link NavigationRequest}. The request itself is built by calling the + * {@link #buildRequest()} method. + * + * @author Petter Holmström + * @since 1.0 + */ + public static final class DefaultPathBuilder extends PathBuilder { + + public DefaultPathBuilder(NavigationRequestBuilder requestBuilder) { + super(requestBuilder); + } + + public DefaultPathBuilder(NavigationRequestBuilder requestBuilder, + List initialPath) { + super(requestBuilder, initialPath); + } + + /** + * Adds the specified view to the path. + */ + public DefaultPathBuilder addViewToPath(View view) { + getPath().add(view); + return this; + } + + /** + * Adds the specified views to the path. + */ + public DefaultPathBuilder addViewsToPath(View... views) { + getPath().addAll(Arrays.asList(views)); + return this; + } + } + + /** + * Sets the value of a single parameter to be passed to the view. + */ + public NavigationRequestBuilder

setParam(String paramName, + Object paramValue) { + params.put(paramName, paramValue); + return this; + } + + /** + * Sets the values of multiple parameters to be passed to the view. + */ + public NavigationRequestBuilder

setParams(Map params) { + this.params.putAll(params); + return this; + } + + /** + * Returns a {@link PathBuilder} that starts from the previous view (i.e. + * the view behind the current view) of the specified view controller. This + * path can be used to perform a "go back" navigation. + * + * @throws IllegalStateException + * if there are less than two views in the controller's stack, + * or if another path builder has already been created. + */ + public P startWithPathToPreviousView(NavigationController controller) + throws IllegalStateException { + if (controller.getViewStack().size() < 2) { + throw new IllegalStateException( + "Not enough views in controller to start from the previous view"); + } + verifyPathBuilderNotSet(); + pathBuilder = createPathBuilder(controller.getViewStack().subList(0, + controller.getViewStack().size() - 1)); + return pathBuilder; + } + + /** + * Returns a {@link PathBuilder} that starts from the first view of the + * specified view controller. This path can be used to perform a "go home" + * navigation. + * + * @throws IllegalStateException + * if the controller's stack is empty, or if another path + * builder has already been created. + */ + public P startWithPathToFirstView(NavigationController controller) + throws IllegalStateException { + if (controller.isEmpty()) { + throw new IllegalStateException( + "Controller is empty, cannot start from the first view"); + } + verifyPathBuilderNotSet(); + pathBuilder = createPathBuilder(controller.getViewStack().subList(0, 1)); + return pathBuilder; + } + + /** + * Returns a {@link PathBuilder} that starts from the current view of the + * specified view controller. This path can be used when adding a new view + * to the stack. If the controller is empty, this call has the same effect + * as using {@link #startWithEmptyPath()}. + * + * @throws IllegalStateException + * if another path builder has already been created. + */ + public P startWithPathToCurrentView(NavigationController controller) + throws IllegalStateException { + verifyPathBuilderNotSet(); + pathBuilder = createPathBuilder(controller.getViewStack()); + return pathBuilder; + } + + /** + * Returns a {@link PathBuilder} that starts from the path to the specified + * view. + * + * @throws IllegalStateException + * if another path builder has already been created or the + * specified view cannot be found in the controller. + */ + public P startWithPathToView(NavigationController controller, View view) + throws IllegalStateException { + verifyPathBuilderNotSet(); + LinkedList path = new LinkedList(); + boolean found = false; + for (View viewInPath : controller.getViewStack()) { + path.add(viewInPath); + if (viewInPath.equals(view)) { + found = true; + break; + } + } + if (!found) { + throw new IllegalStateException("View not found in controller"); + } + pathBuilder = createPathBuilder(path); + return pathBuilder; + } + + /** + * Returns a {@link PathBuilder} that starts with an empty path. At least + * one view has to be added before the navigation request can be built. + * + * @throws IllegalStateException + * if another path builder has already been created. + */ + public P startWithEmptyPath() throws IllegalStateException { + verifyPathBuilderNotSet(); + pathBuilder = createPathBuilder(); + return pathBuilder; + } + + private void verifyPathBuilderNotSet() throws IllegalStateException { + if (pathBuilder != null) { + throw new IllegalStateException( + "A pathBuilder has already been created"); + } + } + + /** + * Returns a new navigation request builder instance that uses the default + * path builder. + */ + public static NavigationRequestBuilder newInstance() { + return new NavigationRequestBuilder( + DefaultPathBuilder.class); + } + + /** + * Returns a new navigation request builder instance that uses a path + * builder of the specified class. + */ + public static

NavigationRequestBuilder

newInstance( + Class

pathBuilderClass) { + return new NavigationRequestBuilder

(pathBuilderClass); + } +}