dermoritz
dermoritz

Reputation: 13011

Places that share all but prefix or how to use PlaceHistoryMapperWithFactory

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

Answers (2)

Thomas Broyer
Thomas Broyer

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

dermoritz
dermoritz

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

Related Questions