Reputation: 13011
In my gwt-app i have some places that share all but the prefix (like "editUserPlace" and "showUserPlace" - the state is determined by userId in this case) My current attempt is to extend an abstract "UserPlace" by "ShowUserPlace" and "EditUserPlace" they differ only in one line: @Prefix("showUser")/ @Prefix("editUser") - the hole tokenizer code must be copied (i can not inherit the tokenizers code but override the prefix).
in https://groups.google.com/d/topic/google-web-toolkit/pghMLX27Y4Y/discussion thomas suggested to use "PlaceHistoryMapperWithFactory" but i am stuck with it.
Do i have to provide a method for each place/tokenizer (also for places that are "normal" - provideing their own tokenizers)? Do i have to ad my Abstract and/or the extending classes to @WithTokenizer? How/where should i call setFactory?
Does anybody used PlaceHistoryMapperWithFactory (probably in similar use case)? And giv some advice? Does anybody faced the same issue and solved it another way?
Upvotes: 0
Views: 999
Reputation: 64541
That factory should work:
class MyFactory {
@Prefix("showUser")
public PlaceTokenizer<ShowUserPlace> showUserPlace() {
return new UserPlaceTokenizer<ShowUserPlace>() {
protected ShowUserPlace createPlace(String id) {
return new ShowUserPlace(id);
}
};
}
@Prefix("showUser")
public PlaceTokenizer<EditUserPlace> showUserPlace() {
return new UserPlaceTokenizer<EditUserPlace>() {
protected EditUserPlace createPlace(String id) {
return new EditUserPlace(id);
}
};
}
}
abstract class UserPlaceTokenizer<P extends UserPlace> implements PlaceTokenizer<P> {
public P getPlace(String token) {
// shared logic between both places: parses ID (or whatever) from token
return createPlace(id);
}
public String getToken(P place) {
// shared logic between both places: build token out of place
return token;
}
protected abstract P createPlace(String id);
}
Of course you could also inject some sort of Provider<P>
in the tokenizer instead of subclassing it to override its createPlace
method.
You can use it along with @WithTokenizers
, the generator will choke if it ever finds two tokenizers for the exact same place or prefix.
You should call setFactory
just after you GWT.create()
your mapper (actually, what matters is you set the factory before any call to the mapper's getPlace
or getToken
methods).
Upvotes: 1
Reputation: 13011
Ok i fiddled around with an generic tokenizer ("UserPlaceTokenizer") but gave up: The problem is that the Factory needs Tokenizers of a concrete class so first i tried to make a generic Tokenizer:
public static class Tokenizer<T extends UserPlace> implements PlaceTokenizer<T> {
@Override
public final T getPlace(final String token) {
return (T) new UserPlace(token); //BAD
}
@Override
public final String getToken(final T place) {
return place.getToken();
}
}
The Problem is the cast of UserPlace to (T) - that won't work (UserPlace cannot be cast to a concrete sub class). So i need a "return new ShowUserPlace(...)" somewhere to get this instance, to satisfy the tokenizer interface. To make o long story not too much longer: i am back with my previous solution: The extending classes are copying the superclass' constructor and provide separate Tokenizers that simply call their own constructor (that calls the super constructor):
public class ShowUserPlace extends UserPlace {
public ShowUserPlace(String token) {
super(token);
}
@Prefix(value = "showUser")
public static class Tokenizer implements PlaceTokenizer<ShowUserPlace> {
@Override
public final ShowUserPlace getPlace(final String token) {
return new ShowUserPlace(token);
}
@Override
public final String getToken(final ShowUserPlace place) {
return place.getToken();
}
}
}
An interim solution was an abstract tokenizer that provides the interface' methods. getPlace will call an abstract method (that returns T) and the concrete implementation will call the concrete constructor (that also only calls the super constructor). At the end this solution will have about the same number of lines and copied code as the solution above :-|.
I still have a bad feeling about this - probably their is still a solution with "..WithFactory" or a completely different way to face this kind of use case.
Upvotes: 0