In r8817, we landed a patch that allows @Prefix(""), i.e. a
non-prefixed PlaceTokenizer, and produces a "separator-less" history
token as a result. The token parsing however wasn't updated, and such
a token would always go the fallback route to the default place,
without ever being "parsed".

See also: http://code.google.com/p/google-web-toolkit/issues/detail?id=5899

Review by: robertvawter@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9635 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/place/impl/AbstractPlaceHistoryMapper.java b/user/src/com/google/gwt/place/impl/AbstractPlaceHistoryMapper.java
index 1fb4ae2..c8399db 100644
--- a/user/src/com/google/gwt/place/impl/AbstractPlaceHistoryMapper.java
+++ b/user/src/com/google/gwt/place/impl/AbstractPlaceHistoryMapper.java
@@ -51,13 +51,18 @@
   
   public Place getPlace(String token) {
     int colonAt = token.indexOf(':');
-    if (colonAt > 0) {
-      String initial = token.substring(0, colonAt);
-      String rest = token.substring(colonAt + 1);
-      PlaceTokenizer<?> tokenizer = getTokenizer(initial);
-      if (tokenizer != null) {
-        return tokenizer.getPlace(rest);
-      }
+    String initial;
+    String rest;
+    if (colonAt >= 0) {
+      initial = token.substring(0, colonAt);
+      rest = token.substring(colonAt + 1);
+    } else {
+      initial = "";
+      rest = token;
+    }
+    PlaceTokenizer<?> tokenizer = getTokenizer(initial);
+    if (tokenizer != null) {
+      return tokenizer.getPlace(rest);
     }
     return null;
   }
diff --git a/user/test/com/google/gwt/place/impl/PlaceHistoryMapperGeneratorTest.java b/user/test/com/google/gwt/place/impl/PlaceHistoryMapperGeneratorTest.java
index c17e15a..402477b 100644
--- a/user/test/com/google/gwt/place/impl/PlaceHistoryMapperGeneratorTest.java
+++ b/user/test/com/google/gwt/place/impl/PlaceHistoryMapperGeneratorTest.java
@@ -28,6 +28,7 @@
 import com.google.gwt.place.testplaces.Place3;
 import com.google.gwt.place.testplaces.Place4;
 import com.google.gwt.place.testplaces.Place5;
+import com.google.gwt.place.testplaces.Place6;
 import com.google.gwt.place.testplaces.Tokenizer2;
 import com.google.gwt.place.testplaces.Tokenizer3;
 import com.google.gwt.place.testplaces.Tokenizer4;
@@ -39,11 +40,11 @@
 public class PlaceHistoryMapperGeneratorTest extends GWTTestCase {
   @WithTokenizers({
       Place1.Tokenizer.class, Tokenizer2.class, Tokenizer3.class,
-      Tokenizer4.class})
+      Tokenizer4.class, Place6.Tokenizer.class})
   interface LocalNoFactory extends PlaceHistoryMapper {
   };
 
-  @WithTokenizers(Tokenizer4.class)
+  @WithTokenizers({Tokenizer4.class, Place6.Tokenizer.class})
   interface LocalWithFactory extends
       PlaceHistoryMapperWithFactory<TokenizerFactory> {
   };
@@ -71,6 +72,7 @@
   Place3 place3 = new Place3("charlie");
   Place4 place4 = new Place4("delta");
   Place5 place5 = new Place5("echo");
+  Place6 place6 = new Place6("foxtrot");
 
   public void testTopLevelWithoutFactory() {
     AbstractPlaceHistoryMapper<?> subject = GWT.create(NoFactory.class);
@@ -148,6 +150,12 @@
       assertTrue(subject.getTokenizer("Place3") instanceof Tokenizer3);
     }
     assertTrue(subject.getTokenizer("Place4") instanceof Tokenizer4);
+    
+    // Empty prefix
+    String history6 = subject.getPrefixAndToken(place6).toString();
+    assertEquals(place6.content, history6);
+    assertTrue(subject.getTokenizer("") instanceof Place6.Tokenizer);
+    assertTrue(subject.getPlace("noPrefix") instanceof Place6);
 
     Place place = new Place() {
     };
diff --git a/user/test/com/google/gwt/place/rebind/PlaceHistoryGeneratorContextTest.java b/user/test/com/google/gwt/place/rebind/PlaceHistoryGeneratorContextTest.java
index 3c639fc..c53795f 100644
--- a/user/test/com/google/gwt/place/rebind/PlaceHistoryGeneratorContextTest.java
+++ b/user/test/com/google/gwt/place/rebind/PlaceHistoryGeneratorContextTest.java
@@ -39,6 +39,7 @@
 import com.google.gwt.place.testplaces.Place2;
 import com.google.gwt.place.testplaces.Place3;
 import com.google.gwt.place.testplaces.Place4;
+import com.google.gwt.place.testplaces.Place6;
 import com.google.gwt.place.testplaces.Tokenizer2;
 import com.google.gwt.place.testplaces.Tokenizer3;
 import com.google.gwt.place.testplaces.Tokenizer4;
@@ -83,6 +84,7 @@
     rtn.add(new RealJavaResource(Place2.class));
     rtn.add(new RealJavaResource(Place3.class));
     rtn.add(new RealJavaResource(Place4.class));
+    rtn.add(new RealJavaResource(Place6.class));
     rtn.add(new RealJavaResource(Tokenizer2.class));
     rtn.add(new RealJavaResource(Tokenizer3.class));
     rtn.add(new RealJavaResource(Tokenizer4.class));
@@ -133,16 +135,17 @@
     JClassType place2 = typeOracle.getType(Place2.class.getName());
     JClassType place3 = typeOracle.getType(Place3.class.getName());
     JClassType place4 = typeOracle.getType(Place4.class.getName());
+    JClassType place6 = typeOracle.getType(Place6.class.getName());
 
     PlaceHistoryGeneratorContext context = createContext(TreeLogger.NULL,
         typeOracle, NoFactory.class.getName(), null);
 
     // Found all place prefixes?
-    assertEquals(new HashSet<String>(Arrays.asList(Place1.Tokenizer.PREFIX,
+    assertEquals(new HashSet<String>(Arrays.asList("", Place1.Tokenizer.PREFIX,
         "Place2", "Place3", "Place4")), context.getPrefixes());
 
     // Found all place types and correctly sorted them?
-    assertEquals(Arrays.asList(place3, place4, place1, place2),
+    assertEquals(Arrays.asList(place3, place4, place1, place2, place6),
         new ArrayList<JClassType>(context.getPlaceTypes()));
 
     // correctly maps place types to their prefixes?
@@ -150,12 +153,14 @@
     assertEquals("Place2", context.getPrefix(place2));
     assertEquals("Place3", context.getPrefix(place3));
     assertEquals("Place4", context.getPrefix(place4));
+    assertEquals("", context.getPrefix(place6));
 
     // there obviously shouldn't be factory methods
     assertNull(context.getTokenizerGetter(Place1.Tokenizer.PREFIX));
     assertNull(context.getTokenizerGetter("Place2"));
     assertNull(context.getTokenizerGetter("Place3"));
     assertNull(context.getTokenizerGetter("Place4"));
+    assertNull(context.getTokenizerGetter(""));
 
     // correctly maps prefixes to their tokenizer type?
     assertEquals(typeOracle.getType(Place1.Tokenizer.class.getCanonicalName()),
@@ -166,6 +171,8 @@
         context.getTokenizerType("Place3"));
     assertEquals(typeOracle.getType(Tokenizer4.class.getName()),
         context.getTokenizerType("Place4"));
+    assertEquals(typeOracle.getType(Place6.Tokenizer.class.getCanonicalName()),
+        context.getTokenizerType(""));
   }
 
   public void testWithFactory() throws UnableToCompleteException,
@@ -177,6 +184,7 @@
     JClassType place2 = typeOracle.getType(Place2.class.getName());
     JClassType place3 = typeOracle.getType(Place3.class.getName());
     JClassType place4 = typeOracle.getType(Place4.class.getName());
+    JClassType place6 = typeOracle.getType(Place6.class.getName());
     JClassType factory = typeOracle.getType(TokenizerFactory.class.getName());
 
     PlaceHistoryGeneratorContext context = createContext(TreeLogger.NULL,
@@ -184,12 +192,12 @@
         TokenizerFactory.class.getName());
 
     // Found all place prefixes?
-    assertEquals(new HashSet<String>(Arrays.asList(Place1.Tokenizer.PREFIX,
+    assertEquals(new HashSet<String>(Arrays.asList("", Place1.Tokenizer.PREFIX,
         TokenizerFactory.PLACE2_PREFIX, "Place3", "Place4")),
         context.getPrefixes());
 
     // Found all place types and correctly sorted them?
-    assertEquals(Arrays.asList(place3, place4, place1, place2),
+    assertEquals(Arrays.asList(place3, place4, place1, place2, place6),
         new ArrayList<JClassType>(context.getPlaceTypes()));
 
     // correctly maps place types to their prefixes?
@@ -197,6 +205,7 @@
     assertEquals(TokenizerFactory.PLACE2_PREFIX, context.getPrefix(place2));
     assertEquals("Place3", context.getPrefix(place3));
     assertEquals("Place4", context.getPrefix(place4));
+    assertEquals("", context.getPrefix(place6));
 
     // correctly map prefixes to their factory method (or null)?
     assertEquals(factory.getMethod("getTokenizer1", EMPTY_JTYPE_ARRAY),
@@ -206,6 +215,7 @@
     assertEquals(factory.getMethod("getTokenizer3", EMPTY_JTYPE_ARRAY),
         context.getTokenizerGetter("Place3"));
     assertNull(context.getTokenizerGetter("Place4"));
+    assertNull(context.getTokenizerGetter(""));
 
     // correctly maps prefixes to their tokenizer type (or null)?
     assertNull(context.getTokenizerType(Place1.Tokenizer.PREFIX));
@@ -213,6 +223,8 @@
     assertNull(context.getTokenizerType("Place3"));
     assertEquals(typeOracle.getType(Tokenizer4.class.getName()),
         context.getTokenizerType("Place4"));
+    assertEquals(typeOracle.getType(Place6.Tokenizer.class.getCanonicalName()),
+        context.getTokenizerType(""));
   }
 
   public void testDuplicatePrefix() {
diff --git a/user/test/com/google/gwt/place/testplacemappers/NoFactory.java b/user/test/com/google/gwt/place/testplacemappers/NoFactory.java
index 39ef9e9..4f96fa5 100644
--- a/user/test/com/google/gwt/place/testplacemappers/NoFactory.java
+++ b/user/test/com/google/gwt/place/testplacemappers/NoFactory.java
@@ -18,6 +18,7 @@
 import com.google.gwt.place.shared.PlaceHistoryMapper;
 import com.google.gwt.place.shared.WithTokenizers;
 import com.google.gwt.place.testplaces.Place1;
+import com.google.gwt.place.testplaces.Place6;
 import com.google.gwt.place.testplaces.Tokenizer2;
 import com.google.gwt.place.testplaces.Tokenizer3;
 import com.google.gwt.place.testplaces.Tokenizer4;
@@ -27,6 +28,6 @@
  */
 @WithTokenizers({
   Place1.Tokenizer.class, Tokenizer2.class, Tokenizer3.class,
-  Tokenizer4.class})
+  Tokenizer4.class, Place6.Tokenizer.class})
 public interface NoFactory extends PlaceHistoryMapper {
 }
\ No newline at end of file
diff --git a/user/test/com/google/gwt/place/testplacemappers/WithFactory.java b/user/test/com/google/gwt/place/testplacemappers/WithFactory.java
index 29976a9..4cddee3 100644
--- a/user/test/com/google/gwt/place/testplacemappers/WithFactory.java
+++ b/user/test/com/google/gwt/place/testplacemappers/WithFactory.java
@@ -17,13 +17,14 @@
 
 import com.google.gwt.place.shared.PlaceHistoryMapperWithFactory;
 import com.google.gwt.place.shared.WithTokenizers;
+import com.google.gwt.place.testplaces.Place6;
 import com.google.gwt.place.testplaces.Tokenizer4;
 import com.google.gwt.place.testplaces.TokenizerFactory;
 
 /**
  * Used by tests of {@link com.google.gwt.place.rebind.PlaceHistoryMapperGenerator}.
  */
-@WithTokenizers(Tokenizer4.class)
+@WithTokenizers({Tokenizer4.class, Place6.Tokenizer.class})
 public interface WithFactory extends
   PlaceHistoryMapperWithFactory<TokenizerFactory> {
 }
\ No newline at end of file
diff --git a/user/test/com/google/gwt/place/testplaces/Place6.java b/user/test/com/google/gwt/place/testplaces/Place6.java
new file mode 100644
index 0000000..4c609c6
--- /dev/null
+++ b/user/test/com/google/gwt/place/testplaces/Place6.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 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.testplaces;
+
+import com.google.gwt.place.shared.Place;
+import com.google.gwt.place.shared.PlaceTokenizer;
+import com.google.gwt.place.shared.Prefix;
+
+/**
+ * Used by tests of {@link com.google.gwt.place.rebind.PlaceHistoryMapperGenerator}.
+ */
+public class Place6 extends Place {
+  public final String content;
+
+  public Place6(String token) {
+    this.content = token;
+  }
+  
+  /**
+   * Tokenizer.
+   */
+  @Prefix("")
+  public static class Tokenizer implements PlaceTokenizer<Place6> {
+    public Place6 getPlace(String token) {
+      return new Place6(token);
+    }
+
+    public String getToken(Place6 place) {
+      return place.content;
+    }
+  }
+}