Package org.eurekastreams.web.client.history

Source Code of org.eurekastreams.web.client.history.HistoryHandler

/*
* Copyright (c) 2010-2012 Lockheed Martin Corporation
*
* 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 org.eurekastreams.web.client.history;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eurekastreams.server.domain.Page;
import org.eurekastreams.web.client.events.EventBus;
import org.eurekastreams.web.client.events.HistoryViewsChangedEvent;
import org.eurekastreams.web.client.events.Observer;
import org.eurekastreams.web.client.events.PreSwitchedHistoryViewEvent;
import org.eurekastreams.web.client.events.PreventHistoryChangeEvent;
import org.eurekastreams.web.client.events.SwitchedHistoryViewEvent;
import org.eurekastreams.web.client.events.UpdateHistoryEvent;
import org.eurekastreams.web.client.events.UpdateRawHistoryEvent;
import org.eurekastreams.web.client.events.UpdatedHistoryParametersEvent;
import org.eurekastreams.web.client.jsni.WidgetJSNIFacadeImpl;
import org.eurekastreams.web.client.ui.Session;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;

/**
* The HistoryHandler should be the ONLY history listener in the entire app. It should listen for history change events
* and fire off one of two events. An event to indicate the current page/view has changed (which would rerender the page
* but not in a reload) and/or an event to indicate the parameters of the page have changed (which will alert the
* current view of this, not change it. It also eats an UpdateHistoryEvent which allows a view to change the history w/o
* directly touching it, and involves helper methods to create history tokens so that manual Hyperlinks can be made
* without hardcoding URLs.
*
*/
public class HistoryHandler implements ValueChangeHandler<String>
{
    /**
     * The JSNI facade.
     */
    private final WidgetJSNIFacadeImpl jsniFacade = new WidgetJSNIFacadeImpl();

    /**
     * The values.
     */
    private Map<String, String> currentValues = new HashMap<String, String>();

    /**
     * The views.
     */
    private List<String> currentViews = new ArrayList<String>();

    /**
     * The page.
     */
    private Page currentPage = null;

    /**
     * Should we execute the value change.
     */
    private boolean fireValueChange = true;

    /**
     * Should we stop the history change?.
     */
    private boolean interruptHistoryChange = false;

    /**
     * The previous token.
     */
    private String previousToken = "";

    /**
     * Default Constructor.
     */
    public HistoryHandler()
    {
        EventBus eventBus = Session.getInstance().getEventBus();
        eventBus.addObserver(UpdateHistoryEvent.class, new Observer<UpdateHistoryEvent>()
        {
            public void update(final UpdateHistoryEvent event)
            {
                updateHistory(getHistoryToken(event.getRequest()));
            }
        });
        eventBus.addObserver(UpdateRawHistoryEvent.class, new Observer<UpdateRawHistoryEvent>()
        {
            public void update(final UpdateRawHistoryEvent event)
            {
                updateHistory(event.getHistoryToken());
            }
        });
        eventBus.addObserver(PreventHistoryChangeEvent.class, new Observer<PreventHistoryChangeEvent>()
        {
            public void update(final PreventHistoryChangeEvent event)
            {
                interruptHistoryChange = true;
            }
        });
        History.addValueChangeHandler(this);
    }

    /**
     * Handles updating history when requested.
     *
     * @param historyToken
     *            The history token for the new location.
     */
    private void updateHistory(final String historyToken)
    {
        previousToken = History.getToken();
        onValueChange(historyToken);
        fireValueChange = false;
        jsniFacade.setHistoryToken(historyToken, true);

        // in case setting the history token above doesn't cause onValueChange to be called, reset
        // fireValueChange so it will work properly next time around
        fireValueChange = true;
    }

    /**
     * On Value Change.
     *
     * @param historyToken
     *            the history token.
     */
    private void onValueChange(final String historyToken)
    {
        if (fireValueChange)
        {
            // save old state
            List<String> originalViews = currentViews;
            Page originalPage = currentPage;
            Map<String, String> originalValues = currentValues;

            // parse and store new state
            CreateUrlRequest parsed = parseHistoryToken(historyToken);
            currentPage = parsed.getPage();
            currentViews = parsed.getViews();
            currentValues = parsed.getParameters();

            // check if the "views" part has changed
            boolean viewUpdated = false;
            if (originalViews.size() == currentViews.size())
            {
                for (int i = 0; i < currentViews.size(); i++)
                {
                    if (!currentViews.get(i).equals(originalViews.get(i)))
                    {
                        viewUpdated = true;
                        break;
                    }
                }
            }
            else
            {
                viewUpdated = true;
            }
            if (viewUpdated)
            {
                EventBus.getInstance().notifyObservers(new HistoryViewsChangedEvent(currentViews));
            }

            // check if the page has changed
            if (originalPage != currentPage)
            {
                // Let developers know we're about to switch the view. Prep for it if necessary. If you want us
                // to stop, throw a PreventHistoryChangeEvent and we'll set the boolean to stop it from going.
                Session.getInstance().getEventBus()
                        .notifyObservers(new PreSwitchedHistoryViewEvent(currentPage, currentViews));

                // We're all clear. Go ahead. These are the events you're looking for.
                if (!interruptHistoryChange)
                {
                    // Put the original events back into the event bus, wiping out all the events specific to the prior
                    // page.
                    Session.getInstance().getEventBus().restoreBufferedObservers();
                    // Clear all temporary timer jobs.
                    Session.getInstance().getTimer().clearTempJobs();
                    // Tell listeners the URL has indicated a page/view change.

                    Session.getInstance().getEventBus()
                            .notifyObservers(new SwitchedHistoryViewEvent(currentPage, currentViews));
                }
                // A developer has halted the process. He probably sees something he needs to alert the user
                // of before they switch the page. Roll everything back.
                else
                {
                    currentViews = originalViews;
                    currentPage = originalPage;
                    currentValues = originalValues;

                    interruptHistoryChange = false;
                    fireValueChange = false;
                    jsniFacade.setHistoryToken(previousToken, true);
                    fireValueChange = true;

                    return;
                }
            }

            Session.getInstance().getEventBus()
                    .notifyObservers(new UpdatedHistoryParametersEvent(currentValues, viewUpdated));
        }
        fireValueChange = true;
    }

    /**
     * OnValueChange gets called when the history changes.
     *
     * @param event
     *            the event.
     */
    public void onValueChange(final ValueChangeEvent<String> event)
    {
        onValueChange(event.getValue());
    }

    /**
     * Parses a history token into its component parts.
     *
     * @param historyToken
     *            History token.
     * @return Component parts as a CreateUrlRequest.
     */
    public CreateUrlRequest parseHistoryToken(final String historyToken)
    {
        HashMap<String, String> parameters = new HashMap<String, String>();

        String concatenatedViews = historyToken;

        int questionMarkIndex = historyToken.indexOf("?");
        if (questionMarkIndex >= 0)
        {
            concatenatedViews = historyToken.substring(0, questionMarkIndex);

            // get the sub string of parameters var=1&var2=2&var3=3
            String[] paramString = historyToken.substring(questionMarkIndex + 1).split("&");
            for (int i = 0; i < paramString.length; i++)
            {
                String[] substr = paramString[i].split("=");
                if (substr.length == 2)
                {
                    parameters.put(jsniFacade.urlDecode(substr[0]), jsniFacade.urlDecode(substr[1]));
                }
            }
        }

        String[] tokens = concatenatedViews.split("/", 2);
        Page page = Page.toEnum(tokens[0]);
        List<String> views = tokens.length > 1 ? Arrays.asList(tokens[1].split("/")) : Collections.EMPTY_LIST;

        return new CreateUrlRequest(page, views, parameters);
    }

    /**
     * Gets a history token given the params.
     *
     * @param request
     *            the request.
     * @return the token.
     */
    public String getHistoryToken(final CreateUrlRequest request)
    {
        Page inPage = currentPage;
        List<String> inViews = currentViews;

        // determine page
        if (request.getPage() != null)
        {
            inPage = request.getPage();
        }
        if (inPage == null)
        {
            inPage = Page.START;
        }

        // determine views
        if (request.getViews() != null)
        {
            inViews = request.getViews();
        }

        // determine parameters
        Map<String, String> parameters;
        if (request.getReplacePrevious())
        {
            parameters = request.getParameters();
        }
        else
        {
            parameters = new HashMap<String, String>(currentValues);
            for (Entry<String, String> entry : request.getParameters().entrySet())
            {
                if (entry.getValue() == null)
                {
                    parameters.remove(entry.getKey());
                }
                else
                {
                    parameters.put(entry.getKey(), entry.getValue());
                }
            }
        }

        // stringify page and views
        StringBuilder sb = new StringBuilder(inPage.toString());
        for (String view : inViews)
        {
            if (view != null && !view.isEmpty())
            {
                sb.append("/").append(view);
            }
        }

        // stringify parameters
        String prefix = "?";
        for (Entry<String, String> entry : parameters.entrySet())
        {
            sb.append(prefix).append(jsniFacade.urlEncode(entry.getKey())).append("=")
                    .append(jsniFacade.urlEncode(entry.getValue()));
            prefix = "&";
        }

        return sb.toString();
    }

    /**
     * Get the value of a current parameter. NOTE: Do NOT use this to "monitor" the history param, only to grab a one
     * time instance of it. Use the UpdatedHistoryParametersEvent to listen to a parameter.
     *
     * @param key
     *            the key.
     * @return the value.
     */
    public String getParameterValue(final String key)
    {
        return currentValues.get(key);
    }

    /**
     * Gets the views.
     *
     * @return the views.
     */
    public List<String> getViews()
    {
        return currentViews;
    }

    /**
     * @return The current page.
     */
    public Page getPage()
    {
        return currentPage;
    }

    /**
     * @return A collection holding the current history parameters.
     */
    public Map<String, String> getParameters()
    {
        return Collections.unmodifiableMap(currentValues);
    }
}
TOP

Related Classes of org.eurekastreams.web.client.history.HistoryHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.