Thanks, I hadn't thought of that. The way we're minimizing code right now, is by creating some useful abstract tokenizers that handle the common use cases (no parameter tokenizer, key-value pair tokenizer). With those there are corresponding abstract tokenizers that handle creating the hashCode and equals methods.
Aside from the 1:1 between Place and Activity, which bothers me in principle but may not be a practical concern just yet, it's coming along pretty nicely. - Amir On Oct 22, 7:11 am, David Chandler <drfibona...@google.com> wrote: > Thanks for sharing your code, Amir. It's great to see this fleshed > out. One possible simplification is to eliminate PlaceTokenizers > entirely. These are required by the generated PlaceHistoryMapper, but > you are free to create your own implementation of PlaceHistoryMapper > which does not use Tokenizers. This would be inferior from a > dependency injection of view as it still results in a chain of if > statements, but eliminates a lot of code. Here's an example which > doesn't use GIN: > > public class SimplePlaceMapper implements PlaceHistoryMapper { > private ClientFactory clientFactory; > > public SimplePlaceMapper(ClientFactory cf) { > this.clientFactory = cf; > } > > @Override > public Place getPlace(String token) { > if (token.startsWith("helloPlace")) > return new HelloPlace(clientFactory, token); > else if (token.startsWith("goodbyePlace")) > return new GoodbyePlace(clientFactory, token); > return null; > } > > @Override > public String getToken(Place place) { > if (place instanceof ActivityPlace) > return ((ActivityPlace) place).getToken(); > return null; > } > > } > > Note that ActivityPlace has one additional method, getToken(): > > public abstract class ActivityPlace<T extends Activity> extends Place { > public abstract T getActivity(); > public abstract String getToken(); > > } > > You need this in your gwt.xml to turn off the > PlaceHistoryMapperGenerator that will otherwise try to generate a > class replacing SimplePlaceHistoryMapper: > > <replace-with > class="com.google.gwt.sample.hellomvp.client.mvp.SimplePlaceMapper"> > <when-type-assignable > class="com.google.gwt.place.shared.PlaceHistoryMapper" /> > </replace-with> > > You could make a GIN-aware PlaceHistoryMapper, as well, which could > call Provider.get() as you're doing; however, this would require some > gwt.xml hackery because a rebind rule like the above doesn't work with > GIN. At least, I haven't gotten it to work yet... > > /dmc > > > > > > > > On Thu, Oct 21, 2010 at 4:34 PM, Amir Kashani <amirkash...@gmail.com> wrote: > > I work with Tolga, who started the thread on GWTC. Here's the solution > > we came up with based on David's initial suggestion there. > > > 1) We created a base class called ActivityPlace, that has an abstract > > getActivty() method: > > public Activity getActivity(); > > > 2) Thus, the the getActivity method in ActivityMapper is reduced to > > the following: > > > �...@override > > public Activity getActivity(Place place) { > > > if (place instanceof ActivityPlace) { > > return ((ActivityPlace) place).getActivity(); > > } > > > return null; > > } > > > 3) A typical Place then looks like this: > > > public class TestPlace extends ActivityPlace { > > > public static class Tokenizer implements > > PlaceTokenizer<TestPlace> { > > > // Since the place is injectable, we'll let Gin do the > > construction. > > private final Provider<TestPlace> placeProvider; > > > �...@inject > > public Tokenizer(Provider<TestPlace> placeProvider) { > > this.placeProvider = placeProvider; > > } > > > �...@override > > public String getToken(TestPlace place) { > > return null; > > } > > > �...@override > > public TestPlace getPlace(String token) { > > return placeProvider.get(); > > > // If place requires any more work, do it here. > > } > > } > > > private Provider<TestActivity> activityProvider; > > > �...@inject > > public TestPlace(Provider<TestActivity> activityProvider) { > > this.activityProvider = activityProvider; > > } > > > �...@override > > public Activity getActivity() { > > // Can't inject Place into the constructor, so by > > convention, we're using init(Place p) in our Activites to pass the > > place in. > > > return activityProvider.get().init(this); > > } > > > } > > > 4) Then, we create our PlaceHistoryMapperWithFactory: > > public interface AppPlaceHistoryMapper extends > > PlaceHistoryMapperWithFactory<AppPlaceFactory> { // empty } > > > Notice there are no Tokenizer annotations here -- they're no longer > > needed. > > > 5) And the actual factory looks like this: > > > public class AppPlaceFactory { > > > // A single instance of the tokenizer should work, since they > > don't have state. > > �...@inject > > TestPlace.Tokenizer testPlaceTokenizer; > > > �...@inject > > Provider<TestPlace> test; > > > public TestPlace.Tokenizer getTestPlaceTokenizer() { > > return testPlaceTokenizer; > > } > > > // Not required by the factory, but since TestPlace is GIN > > injectable, the constructor might be too complex to construct by hand. > > public TestPlace getTest() { > > return test.get(); > > } > > } > > > I think others may have made their Ginjector the factory -- we opted > > to keep it separate, but it doesn't make much difference. > > > So, after all that, the process for creating a new Place is simply > > create the Place and associated Tokenizer, and add a method inside the > > factory to retrieve the Tokenizer (the generator looks for any no-arg > > methods that return a Tokenizer). > > > What gets more complicated and may prove this approach unscalable is > > that the Place is tied directly to the Activity. There may be some > > scenarios, where PlaceA has a different Activity, depending on the > > ActivityManager and the display region. An ActivityMapper could choose > > to ignore the Place.getActivty() method, but it might be awkward. > > > We'd love some feedback on this and to see what other people are > > doing. > > > - Amir > > > On Oct 21, 12:15 pm, David Chandler <drfibona...@google.com> wrote: > >> Hi Yuan, > > >> Unfortunately, the mere mention of a need for something does not imply > >> its current availability :-) I wrote the Activities and Places doc and > >> really should have left GIN out of it for the time being. The root > >> issue is that GIN does not have a way to createMeA(Foo.class), as such > >> a method might impede the GWT compiler's ability to do whole program > >> optimization as it does today. > > >> Thus, the only way to implement ActivityMapper.getActivity() or > >> PlaceHistoryMapper.getPlace() using GIN would be to return an instance > >> of an Activity or Place that has previously been instantiated by GIN > >> and injected into to the mapper class. In other words, each Activity > >> and Place would have to be a singleton, much like Presenter and Place > >> are in the gwt-presenter framework. But in GWT 2.1, Activity and Place > >> are designed to be disposable, not singletons, which leaves us with > >> the need for "if (place instanceof SomePlace) return new > >> SomePlace()..." It seems like it would be possible to create > >> SomeActivityFactory and SomePlaceFactory classes bound as singletons > >> in GIN gwt-presenter style, which in turn provide newly-created > >> instances of SomeActivity and SomePlace, but that requires lots of > >> boilerplate code... > > >> As for the onerous chain of if statements (which is sounding less > >> onerous all the while), it could be created at compile time using a > >> GWT generator, just as GWT's PlaceHistoryMapperGenerator generates a > >> sub-class of AbstractPlaceHistoryMapper using @WithTokenizers from > >> your PlaceHistoryMapper interface. The advantage of creating your own > >> PlaceHistoryMapper base class and generator would be the ability to > >> pass a ClientFactory or factory-managed objects to newly constructed > >> Places. That is, the generated code could do > > >> if (token.startsWith("SomePlace")) > >> return new SomePlace(clientFactory, token); > >> else if (token.startsWith("AnotherPlace")) > >> return new AnotherPlace(clientFactory, token); > >> ... > > >> Hope that helps someone... > > >> The GWT team is working hard to make this easier in a future point release. > > >> /dmc > > >> On Thu, Oct 21, 2010 at 3:09 AM, Yuan <yuan.w0...@gmail.com> wrote: > >> > can't use gin at ActivityMapper? somehower, on the HelloMVP > >> > AppActivityMapper, > >> > it says > > >> > public Activity getActivity(Place place) { > >> > // This is begging for GIN > >> > if (place instanceof HelloPlace) > >> > return new HelloActivity((HelloPlace) place, > >> > clientFactory); > >> > else if (place instanceof GoodbyePlace) > >> > return new GoodbyeActivity((GoodbyePlace) place, > >> > clientFactory); > > >> > return null; > >> > } > > >> > On Oct 20, 3:22 pm, Thomas Broyer <t.bro...@gmail.com> wrote: > >> >> On 20 oct, 11:42, Sebastian Beigel <sebast...@beigel.de> wrote: > > >> >> > Hi, > > >> >> > I'm looking at 2.1 (RC1) for the first time right now and I try to > >> >> > refactor the hellomvp sample to use GIN. > > >> >> > Unfortunately, I have some problems with the places -> activities > >> >> > mapping. The doc says "A better way to implement the chain of nested > >> >> > ifs would be with a GIN module." and the code is commented "Map each > >> >> > Place to its corresponding Activity. This would be a great use for > >> >> > GIN.". > > >> >> > I agree, but I don't really know how to do this mapping :) Has anyone > >> >> > refactored this code to use GIN? > > >> >> You just can't actually. What could work is using a Ginjector as the > >> >> factory of a PlaceHistoryMapperWithFactory, but for ActivityMapper > >> >> this is not possible (it could be by adding a code generator using a > >> >> factory of activity factories, similar to the factory of place > >> >> tokenizers (which are kind of factories for places) for > >> >> PlaceHistoryMapperWithFactory). > >> >> I wrote an code generator for ActivityMapper some time ago <http://gwt- > >> >> code-reviews.appspot.com/845802/show> it won't do what you're asking > >> >> for but could probably be... > > read more » -- You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group. To post to this group, send email to google-web-tool...@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.