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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> wrote:
>> >> On 20 oct, 11:42, Sebastian Beigel <[email protected]> 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 used as a basis for it. But you'd first have
>> >> to decide how to model a "factory of activities" that would be
>> >> returned by your "Ginjector as a factory for
>> >> ActivityMapperWithFactory".
>>
>> > --
>> > You received this message because you are subscribed to the Google Groups
>> > "Google Web Toolkit" group.
>> > To post to this group, send email to [email protected].
>> > To unsubscribe from this group, send email to
>> > [email protected].
>> > For more options, visit this group
>> > athttp://groups.google.com/group/google-web-toolkit?hl=en.
>>
>> --
>> David Chandler
>> Developer Programs Engineer, Google Web
>> Toolkithttp://googlewebtoolkit.blogspot.com/
>
> --
> You received this message because you are subscribed to the Google Groups
> "Google Web Toolkit" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/google-web-toolkit?hl=en.
>
>
--
David Chandler
Developer Programs Engineer, Google Web Toolkit
http://googlewebtoolkit.blogspot.com/
--
You received this message because you are subscribed to the Google Groups
"Google Web Toolkit" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/google-web-toolkit?hl=en.