blob: e0b37bd92093c0e9c7732d73a75bdf605ed3048b [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* 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.google.gwt.place.shared;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.LegacyHandlerWrapper;
import com.google.gwt.user.client.History;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import java.util.logging.Logger;
/**
* Monitors {@link PlaceChangeEvent}s and
* {@link com.google.gwt.user.client.History} events and keep them in sync.
*/
public class PlaceHistoryHandler {
private static final Logger log = Logger.getLogger(PlaceHistoryHandler.class.getName());
/**
* Default implementation of {@link Historian}, based on {@link History}.
*/
public static class DefaultHistorian implements Historian {
public com.google.gwt.event.shared.HandlerRegistration addValueChangeHandler(
ValueChangeHandler<String> valueChangeHandler) {
return History.addValueChangeHandler(valueChangeHandler);
}
public String getToken() {
return History.getToken();
}
public void newItem(String token, boolean issueEvent) {
History.newItem(token, issueEvent);
}
}
/**
* Optional delegate in charge of History related events. Provides nice
* isolation for unit testing, and allows pre- or post-processing of tokens.
* Methods correspond to the like named methods on {@link History}.
*/
public interface Historian {
/**
* Adds a {@link com.google.gwt.event.logical.shared.ValueChangeEvent}
* handler to be informed of changes to the browser's history stack.
*
* @param valueChangeHandler the handler
* @return the registration used to remove this value change handler
*/
com.google.gwt.event.shared.HandlerRegistration addValueChangeHandler(
ValueChangeHandler<String> valueChangeHandler);
/**
* @return the current history token.
*/
String getToken();
/**
* Adds a new browser history entry. Calling this method will cause
* {@link ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)}
* to be called as well.
*/
void newItem(String token, boolean issueEvent);
}
private final Historian historian;
private final PlaceHistoryMapper mapper;
private PlaceController placeController;
private Place defaultPlace = Place.NOWHERE;
/**
* Create a new PlaceHistoryHandler with a {@link DefaultHistorian}. The
* DefaultHistorian is created via a call to GWT.create(), so an alternative
* default implementation can be provided through &lt;replace-with&gt; rules
* in a {@code gwt.xml} file.
*
* @param mapper a {@link PlaceHistoryMapper} instance
*/
public PlaceHistoryHandler(PlaceHistoryMapper mapper) {
this(mapper, (Historian) GWT.create(DefaultHistorian.class));
}
/**
* Create a new PlaceHistoryHandler.
*
* @param mapper a {@link PlaceHistoryMapper} instance
* @param historian a {@link Historian} instance
*/
public PlaceHistoryHandler(PlaceHistoryMapper mapper, Historian historian) {
this.mapper = mapper;
this.historian = historian;
}
/**
* Handle the current history token. Typically called at application start, to
* ensure bookmark launches work.
*/
public void handleCurrentHistory() {
handleHistoryToken(historian.getToken());
}
/**
* Legacy method tied to the old location for {@link EventBus}.
*
* @deprecated use {@link #register(PlaceController, EventBus, Place)}
*/
@Deprecated
public com.google.gwt.event.shared.HandlerRegistration register(PlaceController placeController,
com.google.gwt.event.shared.EventBus eventBus, Place defaultPlace) {
return new LegacyHandlerWrapper(register(placeController, (EventBus) eventBus, defaultPlace));
}
/**
* Initialize this place history handler.
*
* @return a registration object to de-register the handler
*/
public HandlerRegistration register(PlaceController placeController, EventBus eventBus,
Place defaultPlace) {
this.placeController = placeController;
this.defaultPlace = defaultPlace;
final HandlerRegistration placeReg =
eventBus.addHandler(PlaceChangeEvent.TYPE, new PlaceChangeEvent.Handler() {
public void onPlaceChange(PlaceChangeEvent event) {
Place newPlace = event.getNewPlace();
historian.newItem(tokenForPlace(newPlace), false);
}
});
final HandlerRegistration historyReg =
historian.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String token = event.getValue();
handleHistoryToken(token);
}
});
return new HandlerRegistration() {
public void removeHandler() {
PlaceHistoryHandler.this.defaultPlace = Place.NOWHERE;
PlaceHistoryHandler.this.placeController = null;
placeReg.removeHandler();
historyReg.removeHandler();
}
};
}
/**
* Visible for testing.
*/
Logger log() {
return log;
}
private void handleHistoryToken(String token) {
Place newPlace = null;
if ("".equals(token)) {
newPlace = defaultPlace;
}
if (newPlace == null) {
newPlace = mapper.getPlace(token);
}
if (newPlace == null) {
log().warning("Unrecognized history token: " + token);
newPlace = defaultPlace;
}
placeController.goTo(newPlace);
}
private String tokenForPlace(Place newPlace) {
if (defaultPlace.equals(newPlace)) {
return "";
}
String token = mapper.getToken(newPlace);
if (token != null) {
return token;
}
log().warning("Place not mapped to a token: " + newPlace);
return "";
}
}