/* * 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.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import com.github.peholmst.mvp4vaadin.navigation.ControllableView.HideOperation; /** * This is the default implementation of the {@link ViewController} interface. * It does not require any special initialization, just create a new instance * and start using it. * * @author Petter Holmström * @since 1.0 */ @Deprecated public class DefaultViewController implements ViewController { private static final long serialVersionUID = -8268392494366653976L; private ViewProvider viewProvider; // Package access to make unit testing easier ControllableView currentView; int indexOfCurrentView = -1; Stack<ControllableView> viewStack = new Stack<ControllableView>(); protected Stack<ControllableView> getViewStack() { return viewStack; } @Override public ControllableView getCurrentView() { return currentView; } @Override public ControllableView getFirstView() { return viewStack.isEmpty() ? null : viewStack.firstElement(); } @Override public boolean goBack() { if (viewStack.size() <= 1) { // There is nothing to go back to return false; } ControllableView newView = viewStack.get(indexOfCurrentView - 1); HideOperation operation = currentView.hideView(this, newView, Direction.BACKWARD); if (operation.equals(HideOperation.PREVENT)) { // Current view is not changed return false; } else if (operation.equals(HideOperation.ALLOW)) { // Current view is changed, but stack remains untouched setCurrentView(newView, indexOfCurrentView - 1, null, Direction.BACKWARD); return true; } else { // Current view is changed, stack is modified viewStack.pop(); setCurrentView(newView, indexOfCurrentView - 1, null, Direction.BACKWARD); return true; } } @Override public boolean isBackwardNavigationPossible() { return indexOfCurrentView > 0; } @Override public boolean goForward() { // TODO Implement forward navigation return false; } @Override public boolean isForwardNavigationPossible() { return !viewStack.isEmpty() && currentView != viewStack.peek(); } @Override public boolean goToFirstView() { ControllableView firstView = getFirstView(); if (firstView == null) { return false; } else { return goToView(firstView, null); } } @Override public ViewProvider getViewProvider() { return viewProvider; } @Override public void setViewProvider(ViewProvider viewProvider) { this.viewProvider = viewProvider; } /** * TODO Document me! * * @param viewId * @param userData * @return something * @throws IllegalStateException * @throws IllegalArgumentException */ protected ControllableView getViewFromProvider(String viewId, Map<String, Object> userData) throws IllegalStateException, IllegalArgumentException { if (viewId == null) { throw new IllegalArgumentException("null viewId"); } if (getViewProvider() == null) { throw new IllegalStateException("null viewProvider"); } ControllableView view = null; if (getViewProvider() instanceof InitializingViewProvider) { view = ((InitializingViewProvider) getViewProvider()).getView( viewId, userData); } else { view = getViewProvider().getView(viewId); } if (view == null) { throw new IllegalArgumentException("no such view"); } return view; } @Override public boolean goToView(ControllableView view) { return goToView(view, null); } @Override public boolean goToView(String viewId) throws IllegalStateException, IllegalArgumentException { return goToView(getViewFromProvider(viewId, null)); } private Map<String, Object> buildSimpleUserDataMap(String key, Object value) { Map<String, Object> userData = null; if (key != null) { userData = new HashMap<String, Object>(); userData.put(key, value); } return userData; } @Override public boolean goToView(ControllableView view, String userDataKey, Object userDataValue) { return goToView(view, buildSimpleUserDataMap(userDataKey, userDataValue)); } @Override public boolean goToView(String viewId, String userDataKey, Object userDataValue) throws IllegalStateException, IllegalArgumentException { Map<String, Object> userData = buildSimpleUserDataMap(userDataKey, userDataValue); return goToView(getViewFromProvider(viewId, userData), userData); } @Override public boolean goToView(ControllableView view, Map<String, Object> userData) { if (view == null) { throw new IllegalArgumentException("null view"); } // Check if the view is already the current view if (currentView == view) { return false; } ControllableView oldView = currentView; int indexOfNewViewInStack = viewStack.indexOf(view); if (indexOfNewViewInStack != -1) { // The view already exists in the stack, check if we're moving // forward or backward if (indexOfNewViewInStack < indexOfCurrentView) { // We're moving backward ControllableView viewToHide = currentView; int indexOfViewToHide = indexOfCurrentView; while (viewToHide != view) { HideOperation operation = viewToHide.hideView(this, view, Direction.BACKWARD); if (operation.equals(HideOperation.PREVENT)) { if (viewToHide != oldView) { // We did not get all the way, but the current view // did change // We pass null instead of the user data as this // view is not the intended destination. setCurrentView(viewToHide, indexOfViewToHide, null, Direction.BACKWARD); return true; } else { return false; } } else if (operation.equals(HideOperation.ALLOW)) { // Do not remove any elements from the view stack => // allow forward navigation viewToHide = viewStack.elementAt(--indexOfViewToHide); } else { // Remove elements from stack to prevent forward // navigation viewToHide = viewStack.elementAt(--indexOfViewToHide); viewStack.setSize(indexOfViewToHide + 1); } } setCurrentView(view, indexOfViewToHide, userData, Direction.BACKWARD); } else { // We're moving forward // TODO Implement forward navigation throw new UnsupportedOperationException( "Forward navigation is not yet implemented"); } } else { // We're adding a new view to the stack. // If the current view is not the topmost view, we should clear the // stack. if (!viewStack.isEmpty() && currentView != viewStack.peek()) { do { /* * No hideView() is called here as the views are already hidden and * are in front of the current view, i.e. they have already allowed * the current view to become visible. */ viewStack.pop(); } while (currentView != viewStack.peek()); } if (currentView != null && currentView.hideView(this, view, Direction.FORWARD) .equals(HideOperation.PREVENT)) { return false; } viewStack.push(view); setCurrentView(view, viewStack.size() - 1, userData, Direction.FORWARD); } return true; } protected void setCurrentView(ControllableView view, int indexOfCurrentView, Map<String, Object> userData, Direction direction) { ControllableView oldView = currentView; currentView = view; this.indexOfCurrentView = indexOfCurrentView; view.showView(this, userData, oldView, direction); fireCurrentViewChanged(oldView, view, direction, currentView == viewStack.peek()); } @Override public boolean goToView(String viewId, Map<String, Object> userData) throws IllegalStateException, IllegalArgumentException { return goToView(getViewFromProvider(viewId, userData), userData); } @Override public List<ControllableView> getTrail() { return Collections.unmodifiableList(viewStack); } private final LinkedList<ViewControllerListener> listenerList = new LinkedList<ViewControllerListener>(); /** * TODO Document me! * * @param oldView * @param newView * @param direction * @param newViewIsTopMost */ @SuppressWarnings("unchecked") protected void fireCurrentViewChanged(ControllableView oldView, ControllableView newView, Direction direction, boolean newViewIsTopMost) { /* * Create a clone of the listener list. This way, we prevent weird * situations if any of the listeners register new listeners or remove * existing ones. */ LinkedList<ViewControllerListener> clonedList = (LinkedList<ViewControllerListener>) listenerList .clone(); for (ViewControllerListener listener : clonedList) { listener.currentViewChanged(this, oldView, newView, direction, newViewIsTopMost); } } @Override public void addListener(ViewControllerListener listener) { if (listener != null) { listenerList.add(listener); } } @Override public void removeListener(ViewControllerListener listener) { if (listener != null) { listenerList.remove(listener); } } }