com.google.gwt.gwtpages.client.Pages.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.gwtpages.client.Pages.java

Source

package com.google.gwt.gwtpages.client;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Stack;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.gwtpages.client.event.PageIllegalAccessEvent;
import com.google.gwt.gwtpages.client.event.PageNotFoundEvent;
import com.google.gwt.gwtpages.client.event.PageShownEvent;
import com.google.gwt.gwtpages.client.message.DefaultMessageHandler;
import com.google.gwt.gwtpages.client.page.ApplicationPresenter;
import com.google.gwt.gwtpages.client.page.AsyncPageCallback;
import com.google.gwt.gwtpages.client.page.DefaultPageLoadingHandler;
import com.google.gwt.gwtpages.client.page.LoadedPageContainer;
import com.google.gwt.gwtpages.client.page.Page;
import com.google.gwt.gwtpages.client.page.cache.PageCache;
import com.google.gwt.gwtpages.client.page.cache.SimplePageCache;
import com.google.gwt.gwtpages.client.page.event.PageErrorEventHandler;
import com.google.gwt.gwtpages.client.page.event.PageEventHandler;
import com.google.gwt.gwtpages.client.page.event.PageRequestEventHandler;
import com.google.gwt.gwtpages.client.page.loader.PageLoadCallback;
import com.google.gwt.gwtpages.client.page.loader.PageLoader;
import com.google.gwt.gwtpages.client.page.parameters.PageParameters;
import com.google.gwt.gwtpages.client.page.parameters.SimplePageParameters;
import com.google.gwt.gwtpages.client.page.parameters.SimpleTokenizer;
import com.google.gwt.gwtpages.client.page.parameters.Tokenizer;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.History;

/**
 * Controller class used to move from 1 page to another within a GWT Pages
 * application. When using this class, the
 * {@link GWTPagesSettings#init(PagePresenter, PageLoader, com.google.gwt.event.shared.HandlerManager)}
 * method must be called first. There are several different gotoPage methods
 * which can be used depending on the context.
 * 
 * When the application initially loads, you also usually want to call
 * {@link Pages#showStartPage(boolean)}
 * 
 * @author Joe Hudson
 */
public class Pages implements ValueChangeHandler<String> {

    private static Pages instance;

    private Settings settings = new Settings();
    protected HandlerManager eventBus;

    private boolean keepGoing = true;
    private GotoPageCommand lastCommand;
    private LoadedPageContainer currentPage;

    // local cache
    private Stack<GotoPageCommand> currentCommandStack = new Stack<GotoPageCommand>();
    private Stack<String> currentPageTokenStack = new Stack<String>();
    private static final int MAX_STACK_SIZE = 8;

    public static Pages get() {
        if (null == instance)
            instance = new Pages();
        return instance;
    }

    public static Pages init(PageLoader pageLoader, ApplicationPresenter applicationPresenter, HandlerManager bus,
            boolean monitorHistoryEvents) {
        Pages pages = new Pages(pageLoader, applicationPresenter, bus, monitorHistoryEvents);
        return pages;
    }

    public Pages(PageLoader pageLoader, ApplicationPresenter applicationPresenter, HandlerManager bus,
            boolean monitorHistoryEvents) {
        settings.pagePresenter = applicationPresenter;
        settings.pageLoader = pageLoader;
        eventBus = bus;
        init();
        if (monitorHistoryEvents)
            History.addValueChangeHandler(this);
    }

    protected void init() {
        if (updateStaticInstance()) {
            instance = this;
        }
        settings.pageLoader.init(this);
        settings.pagePresenter.init(this);
    }

    protected boolean updateStaticInstance() {
        return true;
    }

    public Pages() {
        if (null != instance) {
            this.settings = instance.settings;
            this.eventBus = instance.eventBus;
        }
        instance = this;
    }

    /**
     * Return a nested pages instance that can be used within this pages instance
     * @param presenter the nested application presenter
     * @param bus the event bus
     */
    public Pages nested(ApplicationPresenter presenter, HandlerManager bus) {
        return nested(settings.getPageLoader(), presenter, bus);
    }

    /**
     * Return a nested pages instance that can be used within this pages instance
     * @param presenter the nested application presenter
     */
    public Pages nested(ApplicationPresenter presenter) {
        return nested(settings.getPageLoader(), presenter, new HandlerManager(null));
    }

    /**
     * Return a nested pages instance that can be used within this pages instance
     * @param pageLoader the page loader
     * @param presenter the nested application presenter
     * @param bus the event bus
     */
    public Pages nested(PageLoader pageLoader, ApplicationPresenter presenter, HandlerManager bus) {
        Pages pages = new Pages(pageLoader, presenter, bus, false) {
            @Override
            protected boolean updateStaticInstance() {
                return false;
            }
        };
        return pages;
    }

    /**
     * Go to the start page.
     * 
     * @param useHistoryToken
     *            true if the history token should be evaluated to obtain the
     *            page token and false to use the standard default page
     */
    public void showStartPage(boolean useHistoryToken) {
        String token = PageLoader.PAGE_DEFAULT;
        if (useHistoryToken)
            token = History.getToken();
        if (null == token)
            token = PageLoader.PAGE_DEFAULT;
        onNewPage(token, true);
    }

    /**
     * Return the {@link GotoPageCommand} used to navigate to the page
     * represented by the page token
     * 
     * @param pageToken
     *            the page token
     */
    public GotoPageCommand goTo(String pageToken) {
        return new GotoPageCommand(pageToken, this);
    }

    /**
     * Return the {@link GotoPageCommand} used to navigate to the page
     * represented by the page token
     * 
     * @param pageToken
     *            the page token
     * @param convienance
     *            method for using
     *            {@link GotoPageCommand#addParameter(Serializable)}
     */
    public GotoPageCommand goTo(String pageToken, Serializable... params) {
        GotoPageCommand cmd = new GotoPageCommand(pageToken, this);
        for (Serializable param : params)
            cmd.addParameter(param);
        return cmd;
    }

    /**
     * Return the {@link GotoPageCommand} used to navigate to the page
     * represented by the page token
     * 
     * @param pageToken
     *            the page token
     * @param session
     *            a custom page request session
     */
    public GotoPageCommand goTo(String pageToken, PageRequestSession session) {
        return new GotoPageCommand(pageToken, session, this);
    }

    /**
     * Return the {@link GotoPageCommand} used to navigate to the page
     * represented by the page token
     * 
     * @param pageToken
     *            the page token
     * @param session
     *            a custom page request session
     * @param convienance
     *            method for using
     *            {@link GotoPageCommand#addParameter(Serializable)}
     */
    public GotoPageCommand goTo(String pageToken, PageRequestSession session, Serializable... params) {
        GotoPageCommand cmd = new GotoPageCommand(pageToken, session, this);
        for (Serializable param : params)
            cmd.addParameter(param);
        return cmd;
    }

    /**
     * Use {@link GotoPageCommand#execute()}
     */
    void goTo(GotoPageCommand command) {
        currentCommandStack.add(command);
        sanityCheck();

        keepGoing = true;
        for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
            handler.onPageRequest(command.getPageToken(), null, command.getSession());
        }
        String pageToken = command.getPageToken();
        LoadedPageContainer pageData = settings.getPageCache().borrowPage(pageToken);
        String historyToken = null;
        PageParameters parameters = null;
        if (null != command.getParameterList(false)) {
            Serializable[] params = command.getParameterList(false)
                    .toArray(new Serializable[command.getParameterList(false).size()]);
            historyToken = createHistoryToken(pageToken, params);
            parameters = new SimplePageParameters(pageToken, historyToken, params, null);
        } else if (null != command.getParameterMap(false)) {
            historyToken = createHistoryToken(pageToken, command.getParameterMap(false));
            parameters = new SimplePageParameters(pageToken, historyToken, null, command.getParameterMap(false));
        } else {
            historyToken = createHistoryToken(pageToken);
            parameters = new SimplePageParameters(pageToken, historyToken, null, null);
        }

        if (command.shouldAddHistoryToken())
            History.newItem(historyToken, false);

        if (null != pageData) {
            goTo(pageData, parameters, command);
        } else {
            settings.getPageLoader().getPage(pageToken, new _PageLoadCallback(parameters, command));
        }
    }

    private void onNewPage(String historyToken, boolean addHistoryToken) {
        currentPageTokenStack.add(historyToken);
        // FIXME add sanity check here

        keepGoing = true;
        for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
            handler.onPageRequest(null, historyToken, null);
        }
        if (!keepGoing())
            return;
        String pageToken = null;
        if (null == historyToken || historyToken.equals(PageLoader.PAGE_DEFAULT)) {
            pageToken = PageLoader.PAGE_DEFAULT;
        } else {
            Iterator<String> possibleTokens = settings.getTokenizer().getPossiblePageTokens(historyToken);
            while (possibleTokens.hasNext()) {
                String token = possibleTokens.next();
                if (settings.getPageLoader().isValidPageToken(token)) {
                    pageToken = token;
                    break;
                }
            }
        }
        if (null == pageToken) {
            onPageNotFound(null, historyToken);
            return;
        }

        PageParameters parameters = settings.getTokenizer().getPageParameters(historyToken, pageToken);
        LoadedPageContainer pageLoadResult = settings.getPageCache().borrowPage(pageToken);
        if (null != pageLoadResult) {
            // the page was cached
            goTo(pageLoadResult, parameters, new GotoPageCommand(pageToken, this).addHistoryToken(addHistoryToken));
        } else {
            // the page couldn't be found
            settings.getPageLoader().getPage(pageToken, new _PageLoadCallback(parameters,
                    new GotoPageCommand(pageToken, this).addHistoryToken(addHistoryToken)));
        }
    }

    private void sanityCheck() {
        if (currentCommandStack.size() > MAX_STACK_SIZE || currentPageTokenStack.size() > MAX_STACK_SIZE) {
            _forwardToPage(new UnhandledStackOverflowErrorPage());
            stopRequest();
        }
    }

    /**
     * Internal check - should we continue with the page request?
     */
    private boolean keepGoing() {
        if (keepGoing)
            return true;
        else
            return false;
    }

    /** Async page loading callbacks **/
    private void onPageFound(LoadedPageContainer pageLoadResult, PageParameters pageParameters,
            GotoPageCommand command) {
        for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
            handler.onPageLoaded(pageLoadResult);
        }
        if (!keepGoing())
            return;
        settings.getPageCache().registerPage(pageLoadResult.getPageToken(), pageLoadResult);
        goTo(pageLoadResult, pageParameters, command);
    }

    private void onPageNotFound(String pageToken, String historyToken) {
        ArrayList<Command> commands = new ArrayList<Command>();
        if (null == historyToken)
            historyToken = pageToken;
        for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
            Command cmd = handler.onPageNotFound(historyToken);
            if (null != cmd && !commands.contains(cmd))
                commands.add(cmd);
        }
        eventBus.fireEvent(new PageNotFoundEvent(historyToken));
        if (commands.size() == 0) {
            if (settings.getPageLoader().isValidPageToken(PageLoader.PAGE_NOT_FOUND)) {
                goTo(PageLoader.PAGE_NOT_FOUND);
            } else {
                _forwardToPage(new UnhandledPageNotFoundPage(historyToken));
            }
        } else {
            for (Command cmd : commands)
                cmd.execute();
        }
        currentCommandStack.clear();
        currentPageTokenStack.clear();
    }

    private void onPageLoadFailure(String pageToken, String historyToken, Throwable cause) {
        ArrayList<Command> commands = new ArrayList<Command>();
        for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
            Command cmd = handler.onPageLoadFailure(historyToken, cause);
            if (null != cmd && !commands.contains(cmd))
                commands.add(cmd);
        }
        if (commands.size() == 0) {
            _forwardToPage(new UnhandledPageLoadErrorPage(pageToken, cause));
        } else {
            for (Command cmd : commands)
                cmd.execute();
        }
        currentCommandStack.clear();
        currentPageTokenStack.clear();
    }

    private void goTo(LoadedPageContainer page, PageParameters parameters, GotoPageCommand command) {
        // do a sanity check here so we don't get caught in a loop...

        try {
            page.getPage().init(this);
        } catch (Throwable t) {
            for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
                handler.onPageEnterFailure(page, parameters, command);
            }
            return;
        }
        command.getSession().setPageAttributes(page.getAttributes());
        page = page.copy();
        lastCommand = command;
        for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
            handler.onBeforePageEnter(page, parameters, command);
        }
        if (!keepGoing())
            return;
        LoadedPageContainer previousPage = null;
        try {
            _AsyncPageCallback callback = new _AsyncPageCallback(page, previousPage, parameters, command);
            page.getPage().onEnterPage(parameters, command.getSession(), callback);
            for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
                handler.onAfterPageEnter(page, parameters, command);
            }
            if (callback.waitForAsync || callback.endState) {
                if (!callback.waitForAsync) {
                    try {
                        settings.getPageCache().returnPage(page, false);
                    } catch (Throwable t) {
                    }
                }
                // don't do anything - it will happen in the callback onSuccess
                // method
                // or we are at some end state
            } else {
                // complete the process
                callback.successComplete = true;
                callback.onSuccess(true);

                currentCommandStack.clear();
                currentPageTokenStack.clear();
                try {
                    settings.getPageCache().returnPage(page, false);
                } catch (Throwable t) {
                }
            }
        } catch (Throwable t) {
            try {
                settings.getPageCache().returnPage(page, true);
            } catch (Throwable t2) {
            }
            ArrayList<Command> commands = new ArrayList<Command>();
            for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
                Command cmd = handler.onPageEnterFailure(page, parameters, command);
                if (null != cmd && !commands.contains(cmd))
                    commands.add(cmd);
            }
            if (commands.size() == 0) {
                if (settings.getPageLoader().isValidPageToken(PageLoader.PAGE_ERROR)) {
                    goTo(PageLoader.PAGE_ERROR);
                } else {
                    _forwardToPage(new UnhandledPageEnterErrorPage(page.getPageToken(), t));
                }
            } else {
                for (Command cmd : commands)
                    cmd.execute();
            }
            currentCommandStack.clear();
            currentPageTokenStack.clear();
        }
    }

    /**
     * History token change listener
     */
    public void onValueChange(ValueChangeEvent<String> event) {
        onNewPage(event.getValue(), true);
    }

    /**
     * Create and return the history token represented by the page token and
     * additional parameters
     * 
     * @param pageToken
     *            the page token
     * @param parameters
     *            input parameters
     */
    public String createHistoryToken(String pageToken, Serializable... parameters) {
        return settings.getTokenizer().createHistoryToken(pageToken, parameters);
    }

    /**
     * Create and return the history token represented by the page token and
     * additional parameters
     * 
     * @param pageToken
     *            the page token
     * @param parameters
     *            input parameters
     */
    public String createHistoryToken(String pageToken, HashMap<String, Serializable> parameters) {
        return settings.getTokenizer().createHistoryToken(pageToken, parameters);
    }

    /**
     * Stop the current request processing
     */
    public void stopRequest() {
        keepGoing = false;
        currentCommandStack.clear();
        currentPageTokenStack.clear();
    }

    public GotoPageCommand getLastCommand() {
        return lastCommand;
    }

    /**
     * Add page lifecycle event handler
     * 
     * @param pageEventHandlers
     *            the handler(s)
     * @return the Settings
     */
    public Pages add(PageEventHandler... pageEventHandlers) {
        for (PageEventHandler pageEventHandler : pageEventHandlers) {
            pageEventHandler.init(this);
            settings.pageEventHandlers.add(pageEventHandler);
            if (pageEventHandler instanceof PageErrorEventHandler)
                settings.pageErrorEventHandlers.add((PageErrorEventHandler) pageEventHandler);
            if (pageEventHandler instanceof PageRequestEventHandler)
                settings.pageRequestEventHandlers.add((PageRequestEventHandler) pageEventHandler);
        }
        return this;
    }

    /**
     * Set the {@link Tokenizer}. If not set, {@link SimpleTokenizer} will be
     * used.
     * 
     * @param tokenizer
     *            the page tokenizer
     * @return the Settings
     */
    public Pages setPageTokenizer(Tokenizer tokenizer) {
        settings.tokenizer = tokenizer;
        tokenizer.init(this);
        return this;
    }

    /**
     * Set the page cacheing mechanism
     * @param pageCache the page cacheing mechanism
     */
    public void setPageCache(PageCache pageCache) {
        settings.pageCache = pageCache;
    }

    /**
     * Add default event handlers for handling page request messages as well as
     * notifying the user when pages are being loaded. The handlers that are
     * added are
     * <ul>
     * <ol>
     * <li>{@link DefaultPageLoadingHandler}</li>
     * <li>{@link DefaultMessageHandler}</li>
     * </ul>
     */
    public Pages addDefaultEventHandlers() {
        add(new DefaultPageLoadingHandler(), new DefaultMessageHandler());
        return this;
    }

    /**
     * Return the settings for this pages instance
     */
    public Settings getSettings() {
        return settings;
    }

    /**
     * Return the common event bus
     */
    public HandlerManager getEventBus() {
        return eventBus;
    }

    /**
     * Return the {@link}
     * 
     * @return
     */
    public LoadedPageContainer getCurrentPage() {
        return currentPage;
    }

    protected void _forwardToPage(Page page) {
        settings.getApplicationPresenter().showPage(new LoadedPageContainer(page), new SimplePageParameters(this),
                new PageRequestSession());
    }

    /** Async callback classes */
    private class _AsyncPageCallback implements AsyncPageCallback {
        private LoadedPageContainer previousPage;
        private LoadedPageContainer page;
        private PageParameters pageParameters;
        private GotoPageCommand command;
        private boolean successComplete = false;
        private boolean waitForAsync = false;
        private boolean endState = false;

        private _AsyncPageCallback(LoadedPageContainer page, LoadedPageContainer previousPage,
                PageParameters pageParameters, GotoPageCommand command) {
            this.page = page;
            this.previousPage = previousPage;
            this.pageParameters = pageParameters;
            this.command = command;
        }

        public void onSuccess() {
            onSuccess(false);
        }

        private void onSuccess(boolean force) {
            if (!force && successComplete) {
                System.err.println("Error with page '" + page.getPageToken()
                        + "' onSuccess() was called and you must call waitForAsync() first.");
                return;
            }
            try {
                settings.getApplicationPresenter().showPage(page, pageParameters, command.getSession());
                currentPage = page;
                for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
                    handler.onPageEnterSuccess(page, pageParameters, command);
                }
                getEventBus().fireEvent(new PageShownEvent(page, pageParameters, command, previousPage));
                successComplete = true;
                currentCommandStack.clear();
                currentPageTokenStack.clear();
                try {
                    settings.getPageCache().returnPage(page, false);
                } catch (Throwable t) {
                }
            } catch (Throwable t) {
                try {
                    settings.getPageCache().returnPage(page, true);
                } catch (Throwable t2) {
                }
            }
        }

        public void onFailure(Throwable cause) {
            if (successComplete) {
                System.err.println("Error with page '" + page.getPageToken()
                        + "' onFailure() was called but the request was already completed successfully.");
                return;
            }
            try {
                settings.getPageCache().returnPage(page, true);
            } catch (Throwable t) {
            }
            ArrayList<Command> commands = new ArrayList<Command>();
            for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
                Command cmd = handler.onPageEnterFailure(page, pageParameters, command);
                if (null != cmd && !commands.contains(cmd))
                    commands.add(cmd);
            }
            if (commands.size() == 0) {
                if (settings.getPageLoader().isValidPageToken(PageLoader.PAGE_ERROR)) {
                    goTo(PageLoader.PAGE_ERROR);
                } else {
                    _forwardToPage(new UnhandledPageEnterErrorPage(page.getPageToken(), cause));
                }
            } else {
                for (Command cmd : commands)
                    cmd.execute();
            }
        }

        public void onIllegalAccess(Serializable... parameters) {
            try {
                // we don't define this as an error with the page - just user access issue hence the false
                settings.getPageCache().returnPage(page, false);
            } catch (Throwable t) {
            }
            if (successComplete) {
                System.err.println("Error with page '" + page.getPageToken()
                        + "' onIllegalAccess() was called but the request was already completed successfully.");
                return;
            }
            ArrayList<Command> commands = new ArrayList<Command>();
            for (PageErrorEventHandler handler : settings.pageErrorEventHandlers) {
                Command cmd = handler.onIllegalPageAccess(page, pageParameters, command, parameters);
                if (null != cmd && !commands.contains(cmd))
                    commands.add(cmd);
            }
            eventBus.fireEvent(new PageIllegalAccessEvent(page, pageParameters, command, parameters));
            if (commands.size() == 0) {
                if (settings.getPageLoader().isValidPageToken(PageLoader.PAGE_ILLEGAL_ACCESS)) {
                    goTo(PageLoader.PAGE_ILLEGAL_ACCESS);
                } else {
                    _forwardToPage(new UnhandledIllegalAccessPage(page.getPageToken()));
                }
            } else {
                for (Command cmd : commands)
                    cmd.execute();
            }
            endState = true;
        }

        public void waitForAsync() {
            for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
                handler.onPageWaitForAsync(page, pageParameters, command);
            }
            waitForAsync = true;
        }

        public void forward(GotoPageCommand command) {
            try {
                settings.getPageCache().returnPage(page, false);
            } catch (Throwable t) {
            }
            for (PageRequestEventHandler handler : settings.pageRequestEventHandlers) {
                handler.onPageForward(page, pageParameters, this.command, command);
            }
            endState = true;
            command.addHistoryToken(false);
            goTo(command);
        }
    }

    private class _PageLoadCallback implements PageLoadCallback {
        private PageParameters parameters;
        private GotoPageCommand command;

        public _PageLoadCallback(PageParameters parameters, GotoPageCommand command) {
            this.parameters = parameters;
            this.command = command;
        }

        public void onPageFound(LoadedPageContainer pageLoadResult) {
            Pages.this.onPageFound(pageLoadResult, parameters, command);
        }

        public void onPageNotFound(String pageToken) {
            Pages.this.onPageNotFound(pageToken, parameters.getHistoryToken());
        }

        public void onPageLoadFailure(String pageToken, Throwable cause) {
            Pages.this.onPageLoadFailure(pageToken, parameters.getHistoryToken(), cause);
        }
    }

    public class Settings {

        protected ApplicationPresenter pagePresenter;
        protected PageCache pageCache;
        protected Tokenizer tokenizer;
        protected PageLoader pageLoader;
        protected ArrayList<PageEventHandler> pageEventHandlers = new ArrayList<PageEventHandler>();
        protected ArrayList<PageErrorEventHandler> pageErrorEventHandlers = new ArrayList<PageErrorEventHandler>();
        protected ArrayList<PageRequestEventHandler> pageRequestEventHandlers = new ArrayList<PageRequestEventHandler>();

        /**
         * Return the {@link Tokenizer}. If not set, {@link SimpleTokenizer}
         * will be used.
         */
        public Tokenizer getTokenizer() {
            if (null == tokenizer) {
                tokenizer = new SimpleTokenizer();
                tokenizer.init(Pages.this);
            }
            return tokenizer;
        }

        /**
         * Return a list of all defined page lifecycle event handlers (or empty
         * list if none defined)
         */
        public ArrayList<PageEventHandler> getEventHandlers() {
            return pageEventHandlers;
        }

        /**
         * Return the {@link PageLoader}.
         */
        public PageLoader getPageLoader() {
            return pageLoader;
        }

        /**
         * Return the {@link ApplicationPresenter}
         */
        public ApplicationPresenter getApplicationPresenter() {
            return pagePresenter;
        }

        public PageCache getPageCache() {
            if (null == pageCache)
                pageCache = new SimplePageCache();
            return pageCache;
        }
    }
}