James Horsley has uploaded a new change for review.

  https://gwt-review.googlesource.com/3186


Change subject: Adds Map support to RequestFactory (see https://code.google.com/p/google-web-toolkit/issues/detail?id=5524)
......................................................................

Adds Map support to RequestFactory (see
https://code.google.com/p/google-web-toolkit/issues/detail?id=5524)

This is a port of an existing svn patch https://codereview.appspot.com/6132056/
onto the new git repo to work with gerrit.

There's a lot of existing discussion on the patch at
https://codereview.appspot.com/6132056/. From looking back over that discussion the main user facing design decision to call out which isn't just what someone might expect from Map support is that referencing child complex types in keys
and values is done using .with("someMap.keys.property",
"someMap.values.property" )

Change-Id: I6dd3c8a9862473954a72ccee96212c20d9198b22
---
M user/src/com/google/web/bindery/requestfactory/apt/ClientToDomainMapper.java M user/src/com/google/web/bindery/requestfactory/apt/TransportableTypeVisitor.java
M user/src/com/google/web/bindery/requestfactory/server/Resolver.java
M user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java M user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java M user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java M user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java M user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java M user/test/com/google/web/bindery/requestfactory/gwt/client/FindServiceTest.java M user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
A user/test/com/google/web/bindery/requestfactory/server/MapKey.java
A user/test/com/google/web/bindery/requestfactory/server/MapValue.java
M user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
M user/test/com/google/web/bindery/requestfactory/shared/BaseFooProxy.java
A user/test/com/google/web/bindery/requestfactory/shared/MapKeyProxy.java
A user/test/com/google/web/bindery/requestfactory/shared/MapKeyRequest.java
A user/test/com/google/web/bindery/requestfactory/shared/MapValueProxy.java
A user/test/com/google/web/bindery/requestfactory/shared/MapValueRequest.java
M user/test/com/google/web/bindery/requestfactory/shared/SimpleBarProxy.java
M user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
20 files changed, 897 insertions(+), 46 deletions(-)



diff --git a/user/src/com/google/web/bindery/requestfactory/apt/ClientToDomainMapper.java b/user/src/com/google/web/bindery/requestfactory/apt/ClientToDomainMapper.java
index dd1f28c..2eac320 100644
--- a/user/src/com/google/web/bindery/requestfactory/apt/ClientToDomainMapper.java +++ b/user/src/com/google/web/bindery/requestfactory/apt/ClientToDomainMapper.java
@@ -17,6 +17,7 @@

 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 import javax.lang.model.element.ElementKind;
@@ -99,6 +100,12 @@
TypeMirror param = convertSingleParamType(x, state.findType(Collection.class), 0, state); return state.types.getDeclaredType((TypeElement) state.types.asElement(x), param);
     }
+    if (state.types.isAssignable(x, state.findType(Map.class))) {
+      // Convert Map<String,FooProxy> to Map<String,FooDomain>
+ TypeMirror keyParam = convertSingleParamType(x, state.findType(Map.class), 0, state); + TypeMirror valueParam = convertSingleParamType(x, state.findType(Map.class), 1, state); + return state.types.getDeclaredType((TypeElement) state.types.asElement(x), keyParam, valueParam);
+    }
     return defaultAction(x, state);
   }

diff --git a/user/src/com/google/web/bindery/requestfactory/apt/TransportableTypeVisitor.java b/user/src/com/google/web/bindery/requestfactory/apt/TransportableTypeVisitor.java
index 9d47dd1..8663ad5 100644
--- a/user/src/com/google/web/bindery/requestfactory/apt/TransportableTypeVisitor.java +++ b/user/src/com/google/web/bindery/requestfactory/apt/TransportableTypeVisitor.java
@@ -17,6 +17,7 @@

 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 import javax.lang.model.element.ElementKind;
@@ -77,6 +78,19 @@
       }
       return t.getTypeArguments().get(0).accept(this, state);
     }
+    if (state.types.isAssignable(t, state.findType(Map.class))) {
+      if (!allowNestedParameterization) {
+        return false;
+      }
+      allowNestedParameterization = false;
+      DeclaredType asMap =
+          (DeclaredType) State.viewAs(state.findType(Map.class), t, state);
+      if (asMap.getTypeArguments().isEmpty()) {
+        return false;
+      }
+      return t.getTypeArguments().get(0).accept(this, state)
+          && t.getTypeArguments().get(1).accept(this, state);
+    }
     return false;
   }

diff --git a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
index 4658066..b367eb6 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
@@ -37,6 +37,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -45,10 +46,52 @@
* Responsible for converting between domain and client entities. This class has * a small amount of temporary state used to handle graph cycles and assignment
  * of synthetic ids.
- *
+ *
  * @see RequestState#getResolver()
  */
 class Resolver {
+  /**
+   * A parameterized type with key and value parameters.
+   */
+  private static class MapType implements ParameterizedType {
+    private final Class<?> rawType;
+    private final Class<?> keyType;
+    private final Class<?> valueType;
+
+ public MapType(Class<?> rawType, Class<?> keyType, Class<?> valueType) {
+      this.rawType = rawType;
+      this.keyType = keyType;
+      this.valueType = valueType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof MapType)) {
+        return false;
+      }
+      MapType other = (MapType) o;
+      return rawType.equals(other.rawType) && keyType.equals(other.keyType)
+          && valueType.equals(other.valueType);
+    }
+
+    public Type[] getActualTypeArguments() {
+      return new Type[] {keyType, valueType};
+    }
+
+    public Type getOwnerType() {
+      return null;
+    }
+
+    public Type getRawType() {
+      return rawType;
+    }
+
+    @Override
+    public int hashCode() {
+ return rawType.hashCode() * 17 + valueType.hashCode() * 13 + keyType.hashCode() * 7;
+    }
+  }
+
   /**
    * A parameterized type with a single parameter.
    */
@@ -116,9 +159,19 @@
       Class<?> elementType =
ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx)
               .getElementType() : null;
+
+      Class<?> keyType = null;
+      Class<?> valueType = null;
+      if (ctx instanceof MapPropertyContext) {
+        MapPropertyContext mapCtx = (MapPropertyContext) ctx;
+        keyType = mapCtx.getKeyType();
+        valueType = mapCtx.getValueType();
+      }
+
       boolean shouldSend =
isOwnerValueProxy || matchesPropertyRef(propertyRefs, propertyName)
-              || elementType != null && ValueCodex.canDecode(elementType);
+              || elementType != null && ValueCodex.canDecode(elementType)
+ || keyType != null && ValueCodex.canDecode(keyType) && ValueCodex.canDecode(valueType);

       if (!shouldSend) {
         return false;
@@ -132,10 +185,12 @@

       // Turn the domain object into something usable on the client side
       Type type;
-      if (elementType == null) {
-        type = ctx.getType();
-      } else {
+      if (elementType != null) {
         type = new CollectionType(ctx.getType(), elementType);
+      } else if (keyType != null) {
+        type = new MapType(ctx.getType(), keyType, valueType);
+      } else {
+        type = ctx.getType();
       }
       Resolution resolution = resolveClientValue(domainValue, type);
       addPathsToResolution(resolution, propertyName, propertyRefs);
@@ -203,7 +258,7 @@
         // No point trying to follow paths past a null value
         return;
       }
-
+
       // Identity comparison intentional
       if (toResolve == EMPTY) {
         toResolve = new TreeSet<String>();
@@ -318,9 +373,9 @@
    * references.
    */
static boolean matchesPropertyRef(Set<String> propertyRefs, String newPrefix) {
-    /*
+    /*
      * Match all fields for a wildcard
-     *
+     *
* Also, remove list index suffixes. Not actually used, was in anticipation
      * of OGNL type schemes. That said, Editor will slip in such things.
      */
@@ -390,7 +445,7 @@

   /**
* Given a domain object, return a value that can be encoded by the client.
-   *
+   *
* @param domainValue the domain object to be converted into a client-side
    *          value
    * @param assignableTo the type in the client to which the resolved value
@@ -420,7 +475,7 @@

   /**
    * Convert a client-side value into a domain value.
-   *
+   *
    * @param maybeEntityProxy the client object to resolve
* @param detectDeadEntities if <code>true</code> this method will throw a * ReportableException containing a {@link DeadEntityException} if an
@@ -449,6 +504,14 @@
         accumulator.add(resolveDomainValue(o, detectDeadEntities));
       }
       return accumulator;
+    } else if (maybeEntityProxy instanceof Map<?,?>) {
+      Map<Object,Object> accumulator = new HashMap<Object, Object>();
+      for (Entry<?, ?> entry : ((Map<?,?>) maybeEntityProxy ).entrySet()) {
+        accumulator.put(resolveDomainValue(entry.getKey(),
+            detectDeadEntities), resolveDomainValue(entry.getValue(),
+                detectDeadEntities));
+      }
+      return accumulator;
     }
     return maybeEntityProxy;
   }
@@ -456,7 +519,7 @@
   /**
    * Calls {@link Resolution#addPaths(String, Collection)}, enqueuing
    * {@code key} if {@link Resolution#hasWork()} returns {@code true}. This
-   * method will also expand paths on the members of Collections.
+   * method will also expand paths on the members of Collections and Maps.
    */
private void addPathsToResolution(Resolution resolution, String prefix, Set<String> propertyRefs) {
     if (propertyRefs.isEmpty()) {
@@ -470,6 +533,20 @@
       resolution.addPaths(prefix, propertyRefs);
       if (resolution.hasWork()) {
         toProcess.add(resolution);
+      }
+      return;
+    }
+    if (resolution.getClientObject() instanceof Map) {
+      Map<?,?> map = (Map<?, ?>) resolution.getClientObject();
+      for (Map.Entry<?, ?> entry : map.entrySet()) {
+ Resolution keyResolution = clientObjectsToResolutions.get(entry.getKey());
+        if (keyResolution != null) {
+ addPathsToResolution(keyResolution, prefix + ".keys", propertyRefs);
+        }
+ Resolution valueResolution = clientObjectsToResolutions.get(entry.getValue());
+        if (valueResolution != null) {
+ addPathsToResolution(valueResolution, prefix + ".values", propertyRefs);
+        }
       }
       return;
     }
@@ -639,6 +716,15 @@
       return makeResolution(accumulator);
     }

+    if (Map.class.isAssignableFrom(returnClass)) {
+      Map<Object,Object> accumulator = new HashMap<Object,Object>();
+ Type[] entryTypes = TypeUtils.getParameterization(Map.class, clientType);
+      for (Map.Entry<?,?> entry : ((Map<?,?>) domainValue ).entrySet()) {
+ accumulator.put(resolveClientValue(entry.getKey(),entryTypes[0]).getClientObject(), resolveClientValue(entry.getValue(),entryTypes[1]).getClientObject());
+      }
+      return makeResolution(accumulator);
+    }
+
throw new ReportableException("Unsupported domain type " + returnClass.getCanonicalName());
   }
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
index 43b1b82..6bd744a 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java +++ b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
@@ -29,6 +29,7 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 /**
@@ -71,6 +72,9 @@
     if (Set.class.isAssignableFrom(domainClass)) {
       return Set.class.asSubclass(clientClass);
     }
+    if (Map.class.isAssignableFrom(domainClass)) {
+      return Map.class.asSubclass(clientClass);
+    }
     if (TypeUtils.isValueType(domainClass)) {
       return domainClass.asSubclass(clientClass);
     }
@@ -99,6 +103,8 @@
       return List.class;
     } else if (Set.class.equals(clazz)) {
       return Set.class;
+    } else if (Map.class.equals(clazz)) {
+      return Map.class;
     } else if (BaseProxy.class.isAssignableFrom(clazz)) {
       ProxyFor pf = clazz.getAnnotation(ProxyFor.class);
       if (pf != null) {
diff --git a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
index f3fde42..ecca3d7 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java +++ b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
@@ -124,7 +124,7 @@

   /**
    * Process a payload sent by a RequestFactory client.
-   *
+   *
    * @param payload the payload sent by the client
    * @return a payload to return to the client
    */
@@ -527,15 +527,28 @@
                 PropertyContext ctx) {
               // containsKey to distinguish null from unknown
               if (flatValueMap.containsKey(propertyName)) {
-                Class<?> elementType =
- ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx)
-                        .getElementType() : null;
-                Object newValue =
- EntityCodex.decode(state, ctx.getType(), elementType, flatValueMap
-                        .get(propertyName));
- Object resolved = state.getResolver().resolveDomainValue(newValue, false);
+                Object resolved = null;
+ // The null check on getKeyType() is necessary as some of the given PropertyContext's + // implement both MapPropertyContext and CollectionPropertyContext. + if (ctx instanceof MapPropertyContext && ((MapPropertyContext) ctx).getKeyType() != null) {
+                    MapPropertyContext mapCtx = (MapPropertyContext) ctx;
+                    Class<?> keyType = mapCtx.getKeyType();
+                    Class<?> valueType = mapCtx.getValueType();
+                    Object newValue =
+ EntityCodex.decode(state, mapCtx.getType(), keyType,
+                            valueType, flatValueMap.get(propertyName));
+ resolved = state.getResolver().resolveDomainValue(newValue, false);
+                } else {
+                  Class<?> elementType =
+ ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx)
+                          .getElementType() : null;
+                  Object newValue =
+ EntityCodex.decode(state, ctx.getType(), elementType, flatValueMap
+                          .get(propertyName));
+ resolved = state.getResolver().resolveDomainValue(newValue, false);
+                }
                 service.setProperty(domain, propertyName,
-                    service.resolveDomainClass(ctx.getType()), resolved);
+                service.resolveDomainClass(ctx.getType()), resolved);
               }
               return false;
             }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
index 97f783f..4e73a39 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java +++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
@@ -699,7 +699,7 @@

   /**
    * Creates a new proxy with an assigned ID.
-   *
+   *
    * @param clazz The proxy type
    * @param id The id to be assigned to the new proxy
* @param useAppendedContexts if {@code true} use the AutoBeanFactory types associated with any
@@ -911,7 +911,7 @@

   /**
    * Create a new EntityProxy from a snapshot in the return payload.
-   *
+   *
    * @param id the EntityProxyId of the object
    * @param returnRecord the JSON map containing property/value pairs
* @param operations the WriteOperation eventns to broadcast over the EventBus
@@ -932,11 +932,21 @@
           if (ctx.canSet()) {
             if (properties.containsKey(propertyName)) {
               Splittable raw = properties.get(propertyName);
-              Class<?> elementType =
- ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx)
-                      .getElementType() : null;
-              Object decoded =
- EntityCodex.decode(AbstractRequestContext.this, ctx.getType(), elementType, raw);
+              Object decoded = null;
+ if (ctx instanceof MapPropertyContext && ((MapPropertyContext) ctx).getKeyType() != null) {
+                MapPropertyContext mapCtx = (MapPropertyContext) ctx;
+                Class<?> keyType = mapCtx.getKeyType();
+                Class<?> valueType = mapCtx.getValueType();
+                decoded =
+ EntityCodex.decode(AbstractRequestContext.this, mapCtx.getType(), keyType,
+                        valueType, raw);
+              } else {
+                Class<?> elementType =
+ ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx)
+                        .getElementType() : null;
+                decoded =
+ EntityCodex.decode(AbstractRequestContext.this, ctx.getType(), elementType, raw);
+              }
               ctx.set(decoded);
             }
           }
@@ -987,7 +997,7 @@

   /**
    * Get-or-create method for synthetic ids.
-   *
+   *
    * @see #syntheticIds
    */
private <Q extends BaseProxy> SimpleProxyId<Q> allocateSyntheticId(String typeToken, diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java
index f498ac3..ff1d529 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java +++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java
@@ -25,8 +25,10 @@

 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 /**
@@ -115,6 +117,57 @@
   }

   /**
+   * Map decoding follows behaviour of AutoBeanCodexImpl.MapCoder
+   */
+  public static Object decode(EntitySource source,
+ Class<?> type, Class<?> keyType, Class<?> valueType, Splittable split) {
+    if (split == null || split == Splittable.NULL) {
+      return null;
+    }
+
+    if (!Map.class.equals(type)) {
+      throw new UnsupportedOperationException();
+    }
+
+    Map<Object,Object> map = new HashMap<Object,Object>();
+    if (ValueCodex.canDecode(keyType) || !split.isIndexed()) {
+      List<String> keys = split.getPropertyKeys();
+      for (String propertyKey : keys) {
+ Object key = ValueCodex.decode(keyType, StringQuoter.split(propertyKey));
+        if (split.isNull(propertyKey)) {
+          map.put(key, null);
+        } else {
+          Splittable valueSplit = split.get(propertyKey);
+          Object value = null;
+          if (ValueCodex.canDecode(valueType)) {
+            value = ValueCodex.decode(valueType, valueSplit);
+          } else {
+            value = decode(source, valueType, null, valueSplit);
+          }
+          map.put(key,value);
+        }
+      }
+    } else {
+       if (split.size() != 2) {
+         throw new UnsupportedOperationException();
+       }
+       @SuppressWarnings("unchecked")
+ List<Object> keys = (List<Object>) decode(source, List.class, keyType, split.get(0));
+       @SuppressWarnings("unchecked")
+ List<Object> values = (List<Object>) decode(source, List.class, valueType, split.get(1));
+       if (keys.size() != values.size()) {
+         throw new UnsupportedOperationException();
+       }
+
+       for (int i = 0, size = keys.size(); i < size; i++) {
+         map.put(keys.get(i), values.get(i));
+       }
+    }
+
+    return map;
+  }
+
+  /**
    * Create a wire-format representation of an object.
    */
   public static Splittable encode(EntitySource source, Object value) {
@@ -146,6 +199,55 @@
       return StringQuoter.split(toReturn.toString());
     }

+    // Map encoding follows behaviour of AutoBeanCodexImpl.MapCoder
+    if (value instanceof Map<?,?>) {
+      Map<?,?> map = (Map<?, ?>) value;
+      StringBuilder sb = new StringBuilder();
+ boolean isSimpleMap = (map.isEmpty() || ValueCodex.canDecode(map.keySet().iterator().next().getClass()));
+      if (isSimpleMap) {
+        boolean first = true;
+        sb.append("{");
+        for (Map.Entry<?, ?> entry : map.entrySet()) {
+          Object mapKey = entry.getKey();
+          if (mapKey == null) {
+            // A null key in a simple map is meaningless
+            continue;
+          }
+          Object mapValue = entry.getValue();
+
+          if (first) {
+            first = false;
+          } else {
+            sb.append(",");
+          }
+
+ sb.append(StringQuoter.quote(encode(source, mapKey).getPayload()));
+          sb.append(":");
+          if (mapValue == null) {
+            // Null values must be preserved
+            sb.append("null");
+          } else {
+            sb.append(encode(source, mapValue).getPayload());
+          }
+        }
+        sb.append("}");
+      } else {
+        List<Object> keys = new ArrayList<Object>(map.size());
+        List<Object> values = new ArrayList<Object>(map.size());
+        for (Map.Entry<?, ?> entry : map.entrySet()) {
+          keys.add(entry.getKey());
+          values.add(entry.getValue());
+        }
+        sb.append("[");
+        sb.append(encode(source, keys).getPayload());
+        sb.append(",");
+        sb.append(encode(source, values).getPayload());
+        sb.append("]");
+      }
+
+      return StringQuoter.split(sb.toString());
+    }
+
     if (value instanceof BaseProxy) {
AutoBean<BaseProxy> autoBean = AutoBeanUtils.getAutoBean((BaseProxy) value);
       value = BaseProxyCategory.stableId(autoBean);
@@ -157,4 +259,5 @@

     return ValueCodex.encode(value);
   }
+
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java
index d07e43a..3b89b8d 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java +++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java
@@ -139,6 +139,22 @@
             }
           }
         }
+
+        @Override
+ public void endVisitMapProperty(String propertyName, AutoBean<Map<?, ?>> value,
+            MapPropertyContext ctx) {
+           if (value == null) {
+            return;
+          }
+          for (Map.Entry<?,?> entry : value.as().entrySet()) {
+ if (isEntityType(ctx.getKeyType()) || isValueType(ctx.getKeyType())) {
+              serialize((BaseProxy) entry.getKey());
+            }
+ if (isEntityType(ctx.getValueType()) || isValueType(ctx.getValueType())) {
+              serialize((BaseProxy) entry.getValue());
+            }
+          }
+        }
       });
     }

diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/client/FindServiceTest.java b/user/test/com/google/web/bindery/requestfactory/gwt/client/FindServiceTest.java
index 9109f47..d67f291 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/client/FindServiceTest.java +++ b/user/test/com/google/web/bindery/requestfactory/gwt/client/FindServiceTest.java
@@ -15,9 +15,17 @@
  */
 package com.google.web.bindery.requestfactory.gwt.client;

+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import com.google.web.bindery.event.shared.HandlerRegistration;
 import com.google.web.bindery.requestfactory.shared.EntityProxyChange;
 import com.google.web.bindery.requestfactory.shared.EntityProxyId;
+import com.google.web.bindery.requestfactory.shared.MapKeyProxy;
+import com.google.web.bindery.requestfactory.shared.MapValueProxy;
 import com.google.web.bindery.requestfactory.shared.Receiver;
 import com.google.web.bindery.requestfactory.shared.Request;
 import com.google.web.bindery.requestfactory.shared.SimpleBarProxy;
@@ -25,10 +33,6 @@
 import com.google.web.bindery.requestfactory.shared.SimpleFooProxy;
 import com.google.web.bindery.requestfactory.shared.SimpleFooRequest;
 import com.google.web.bindery.requestfactory.shared.SimpleRequestFactory;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;

 /**
* Tests for {@link com.google.web.bindery.requestfactory.shared.RequestFactory}
@@ -40,6 +44,9 @@
    */

   private static final int TEST_DELAY = 5000;
+
+ private static final String[] SIMPLE_FOO_RELATIONS = { "barField","simpleBarKeyMap","simpleBarValueMap",
+    "entityKeyAndValueMap" };

   @Override
   public String getModuleName() {
@@ -143,14 +150,14 @@
   public void testFetchEntityWithRelation() {
     final boolean relationsPresent = true;
     delayTestFinish(TEST_DELAY);
-    req.simpleFooRequest().findSimpleFooById(999L).with("barField").fire(
+ req.simpleFooRequest().findSimpleFooById(999L).with(SIMPLE_FOO_RELATIONS).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
           public void onSuccess(SimpleFooProxy response) {
             checkReturnedProxy(response, relationsPresent);

final EntityProxyId<SimpleFooProxy> stableId = response.stableId(); - req.find(stableId).with("barField").fire(new Receiver<SimpleFooProxy>() { + req.find(stableId).with(SIMPLE_FOO_RELATIONS).fire(new Receiver<SimpleFooProxy>() {

               @Override
               public void onSuccess(SimpleFooProxy returnedProxy) {
@@ -159,6 +166,119 @@
                 finishTestAndReset();
               }
             });
+          }
+        });
+  }
+
+  public void testFetchEntityWithEntityKeyValueMapNoChildren() {
+    delayTestFinish(TEST_DELAY);
+ req.simpleFooRequest().findSimpleFooById(999L).with("entityKeyAndValueMap").fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+
+            assertEquals(1, response.getEntityKeyAndValueMap().size());
+
+            Map.Entry<MapKeyProxy, MapValueProxy> entry =
+ response.getEntityKeyAndValueMap().entrySet().iterator().next();
+
+            assertNotNull(entry.getKey());
+            assertNotNull(entry.getValue());
+
+            assertNull(entry.getKey().getSimple());
+            assertNull(entry.getValue().getSimple());
+
+            finishTestAndReset();
+          }
+        });
+  }
+
+  public void testFetchEntityWithEntityKeyValueMapJustKeyChildren() {
+    delayTestFinish(TEST_DELAY);
+ req.simpleFooRequest().findSimpleFooById(999L).with("entityKeyAndValueMap.keys.simple").fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(1, response.getEntityKeyAndValueMap().size());
+
+            Map.Entry<MapKeyProxy, MapValueProxy> entry =
+ response.getEntityKeyAndValueMap().entrySet().iterator().next();
+
+            assertNotNull(entry.getKey());
+            assertNotNull(entry.getValue());
+
+            assertNotNull(entry.getKey().getSimple());
+            assertNull(entry.getValue().getSimple());
+            finishTestAndReset();
+          }
+        });
+  }
+
+  public void testFetchEntityWithEntityKeyValueMapJustValueChildren() {
+    delayTestFinish(TEST_DELAY);
+ req.simpleFooRequest().findSimpleFooById(999L).with("entityKeyAndValueMap.values.simple").fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(1, response.getEntityKeyAndValueMap().size());
+
+            Map.Entry<MapKeyProxy, MapValueProxy> entry =
+ response.getEntityKeyAndValueMap().entrySet().iterator().next();
+
+            assertNotNull(entry.getKey());
+            assertNotNull(entry.getValue());
+
+            assertNull(entry.getKey().getSimple());
+            assertNotNull(entry.getValue().getSimple());
+            finishTestAndReset();
+          }
+        });
+  }
+
+ public void testFetchEntityWithEntityKeyValueMapBothKeyAndValueChildren() {
+    delayTestFinish(TEST_DELAY);
+ req.simpleFooRequest().findSimpleFooById(999L).with("entityKeyAndValueMap.keys.simple",
+        "entityKeyAndValueMap.values.simple").fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(1, response.getEntityKeyAndValueMap().size());
+
+            Map.Entry<MapKeyProxy, MapValueProxy> entry =
+ response.getEntityKeyAndValueMap().entrySet().iterator().next();
+
+            assertNotNull(entry.getKey());
+            assertNotNull(entry.getValue());
+
+            assertNotNull(entry.getKey().getSimple());
+            assertNotNull(entry.getValue().getSimple());
+            finishTestAndReset();
+          }
+        });
+  }
+
+  public void testFetchEntityWithRelationWithEmptyMaps() {
+    final boolean relationsPresent = true;
+    delayTestFinish(TEST_DELAY);
+ req.simpleFooRequest().findSimpleFooById(999L).with(SIMPLE_FOO_RELATIONS).fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            checkReturnedProxy(response, relationsPresent);
+
+            SimpleFooRequest editRequest = req.simpleFooRequest();
+            SimpleFooProxy editableSimpleFoo = editRequest.edit(response);
+ editableSimpleFoo.setSimpleBarKeyMap( new HashMap<SimpleBarProxy, Integer>()); + editableSimpleFoo.setSimpleBarValueMap( new HashMap<Integer,SimpleBarProxy>()); + editRequest.persistAndReturnSelf().using(editableSimpleFoo).with(SIMPLE_FOO_RELATIONS).fire(
+                new Receiver<SimpleFooProxy>() {
+                  @Override
+                  public void onSuccess(SimpleFooProxy returnedProxy) {
+ assertEquals(0, returnedProxy.getSimpleBarKeyMap().size()); + assertEquals(0, returnedProxy.getSimpleBarValueMap().size());
+                    finishTestAndReset();
+                  }
+                });
           }
         });
   }
@@ -275,10 +395,15 @@
     assertEquals(8L, (long) response.getLongField());
assertEquals(com.google.web.bindery.requestfactory.shared.SimpleEnum.FOO, response
         .getEnumField());
+    assertEquals(3, response.getValueMap().size());
     if (checkForRelations) {
       assertNotNull(response.getBarField());
+      assertEquals(1, response.getSimpleBarKeyMap().size());
+      assertEquals(1, response.getEntityKeyAndValueMap().size());
     } else {
       assertEquals(null, response.getBarField());
+      assertEquals(null, response.getSimpleBarKeyMap());
+      assertEquals(null, response.getEntityKeyAndValueMap());
     }
   }
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java b/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
index caf6670..2ef2859 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java +++ b/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
@@ -49,8 +49,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 import javax.validation.ConstraintViolation;
@@ -62,7 +64,7 @@
 public class RequestFactoryTest extends RequestFactoryTestBase {
   /*
    * DO NOT USE finishTest(). Instead, call finishTestAndReset();
-   *
+   *
* When possible, pass any returned proxies to checkSerialization() and use
    * the return value in the place of the returned object.
    */
@@ -153,7 +155,7 @@
     public void onSuccess(T response) {
       /*
        * Make sure your class path includes:
-       *
+       *
        * tools/lib/apache/log4j/log4j-1.2.16.jar
        * tools/lib/hibernate/validator/hibernate-validator-4.1.0.Final.jar
        * tools/lib/slf4j/slf4j-api/slf4j-api-1.6.1.jar
@@ -252,9 +254,19 @@
     final SimpleFooProxy foo = context.create(SimpleFooProxy.class);
     final SimpleBarProxy bar0 = context.create(SimpleBarProxy.class);
     final SimpleBarProxy bar1 = context.create(SimpleBarProxy.class);
+    final SimpleBarProxy bar2 = context.create(SimpleBarProxy.class);
+    final SimpleBarProxy bar3 = context.create(SimpleBarProxy.class);
     List<SimpleBarProxy> bars = new ArrayList<SimpleBarProxy>();
     bars.add(bar0);
     bars.add(bar1);
+
+ Map<SimpleBarProxy,Integer> barAsKeyMap = new HashMap<SimpleBarProxy, Integer>();
+    barAsKeyMap.put(bar2, 4);
+    barAsKeyMap.put(bar3, 21);
+
+ Map<Integer,SimpleBarProxy> barAsValueMap = new HashMap<Integer,SimpleBarProxy>();
+    barAsValueMap.put(41, bar2);
+    barAsValueMap.put(141, bar3);

     final SimpleFooEventHandler<SimpleBarProxy> handler =
         new SimpleFooEventHandler<SimpleBarProxy>();
@@ -263,14 +275,17 @@
Request<SimpleFooProxy> request = context.persistCascadingAndReturnSelf().using(foo);
     SimpleFooProxy editFoo = context.edit(foo);
     editFoo.setOneToManyField(bars);
+    editFoo.setSimpleBarKeyMap(barAsKeyMap);
+    editFoo.setSimpleBarValueMap(barAsValueMap);
+
     request.fire(new Receiver<SimpleFooProxy>() {
       @Override
       public void onSuccess(SimpleFooProxy response) {
         response = checkSerialization(response);
assertFalse(((SimpleEntityProxyId<SimpleFooProxy>) response.stableId()).isEphemeral());
-        assertEquals(2, handler.persistEventCount); // two bars persisted.
-        assertEquals(2, handler.updateEventCount); // two bars persisted.
-        assertEquals(4, handler.totalEventCount);
+        assertEquals(4, handler.persistEventCount); // four bars persisted.
+        assertEquals(3, handler.updateEventCount); // updates to editFoo
+        assertEquals(7, handler.totalEventCount);
         finishTestAndReset();
       }
     });
diff --git a/user/test/com/google/web/bindery/requestfactory/server/MapKey.java b/user/test/com/google/web/bindery/requestfactory/server/MapKey.java
new file mode 100644
index 0000000..f881755
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/MapKey.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class MapKey {
+
+  /**
+   * DO NOT USE THIS UGLY HACK DIRECTLY! Call {@link #get} instead.
+   */
+ private static Map<String, MapKey> jreTestSingleton = new HashMap<String, MapKey>();
+
+  static {
+    try {
+    reset();
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  public static MapKey findMapKey(String id) {
+    return findMapKeyById(id);
+  }
+
+  /**
+   * Returns <code>null</code> if {@link #findFails} is <code>true</code>.
+   */
+  public static MapKey findMapKeyById(String id) {
+    return get().get(id);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static synchronized Map<String, MapKey> get() {
+    HttpServletRequest req = RequestFactoryServlet.getThreadLocalRequest();
+    if (req == null) {
+      // May be in a JRE test case, use the the singleton
+      return jreTestSingleton;
+    } else {
+      /*
+ * This will not behave entirely correctly unless we have a servlet filter
+       * that doesn't allow any requests to be processed unless they're
+       * associated with an existing session.
+       */
+ Map<String, MapKey> value = (Map<String, MapKey>) req.getSession().getAttribute(
+          MapKey.class.getCanonicalName());
+      if (value == null) {
+        value = resetImpl();
+      }
+      return value;
+    }
+  }
+
+  public static MapKey getSingleton() {
+    return findMapKey("1L");
+  }
+
+  public static void reset() {
+    resetImpl();
+  }
+
+  public static synchronized Map<String, MapKey> resetImpl() {
+    Map<String, MapKey> instance = new HashMap<String, MapKey>();
+    // fixtures
+    MapKey m1 = new MapKey();
+    m1.setId("1L");
+    instance.put(m1.getId(), m1);
+
+    MapKey m2 = new MapKey();
+    m2.setId("999L");
+    instance.put(m2.getId(), m2);
+
+    HttpServletRequest req = RequestFactoryServlet.getThreadLocalRequest();
+    if (req == null) {
+      jreTestSingleton = instance;
+    } else {
+      req.getSession().setAttribute(MapKey.class.getCanonicalName(),
+          instance);
+    }
+    return instance;
+  }
+
+  private String id = "999L";
+  private SimpleValue simple;
+  Integer version = 1;
+
+  public MapKey() {
+    simple = new SimpleValue();
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public SimpleValue getSimple() {
+    return simple;
+  }
+
+  public void setSimple(SimpleValue simple) {
+    this.simple = simple;
+  }
+
+  public Integer getVersion() {
+    return version;
+  }
+
+  public void setVersion(Integer version) {
+    this.version = version;
+  }
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/server/MapValue.java b/user/test/com/google/web/bindery/requestfactory/server/MapValue.java
new file mode 100644
index 0000000..c0cffef
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/MapValue.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.server;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class MapValue {
+  /**
+   * DO NOT USE THIS UGLY HACK DIRECTLY! Call {@link #get} instead.
+   */
+ private static Map<String, MapValue> jreTestSingleton = new HashMap<String, MapValue>();
+
+  static {
+    try {
+    reset();
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  public static MapValue findMapValue(String id) {
+    return findMapValueById(id);
+  }
+
+  /**
+   * Returns <code>null</code> if {@link #findFails} is <code>true</code>.
+   */
+  public static MapValue findMapValueById(String id) {
+    return get().get(id);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static synchronized Map<String, MapValue> get() {
+    HttpServletRequest req = RequestFactoryServlet.getThreadLocalRequest();
+    if (req == null) {
+      // May be in a JRE test case, use the the singleton
+      return jreTestSingleton;
+    } else {
+      /*
+ * This will not behave entirely correctly unless we have a servlet filter
+       * that doesn't allow any requests to be processed unless they're
+       * associated with an existing session.
+       */
+ Map<String, MapValue> value = (Map<String, MapValue>) req.getSession().getAttribute(
+          MapValue.class.getCanonicalName());
+      if (value == null) {
+        value = resetImpl();
+      }
+      return value;
+    }
+  }
+
+  public static MapValue getSingleton() {
+    return findMapValue("1L");
+  }
+
+  public static void reset() {
+    resetImpl();
+  }
+
+  public static synchronized Map<String, MapValue> resetImpl() {
+    Map<String, MapValue> instance = new HashMap<String, MapValue>();
+    // fixtures
+    MapValue s1 = new MapValue();
+    s1.setId("1L");
+    instance.put(s1.getId(), s1);
+
+    MapValue s2 = new MapValue();
+    s2.setId("999L");
+    instance.put(s2.getId(), s2);
+
+    HttpServletRequest req = RequestFactoryServlet.getThreadLocalRequest();
+    if (req == null) {
+      jreTestSingleton = instance;
+    } else {
+      req.getSession().setAttribute(MapValue.class.getCanonicalName(),
+          instance);
+    }
+    return instance;
+  }
+
+  private String id;
+  private SimpleBar simple;
+  Integer version = 1;
+
+  public MapValue() {
+    id = "432234";
+    simple = SimpleBar.getSingleton();
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public SimpleBar getSimple() {
+    return simple;
+  }
+
+  public void setSimple(SimpleBar simple) {
+    this.simple = simple;
+  }
+
+  public Integer getVersion() {
+    return version;
+  }
+
+  public void setVersion(Integer version) {
+    this.version = version;
+  }
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java b/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
index a636432..a3e2b1f 100644
--- a/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
+++ b/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
@@ -124,7 +124,7 @@
     foo3.persist();
     return Arrays.asList(foo1, foo2, foo3);
   }
-
+
   public static SimpleFoo getLongChain() {
     SimpleFoo foo0 = new SimpleFoo();
     SimpleFoo foo1 = new SimpleFoo();
@@ -132,7 +132,7 @@
     SimpleFoo foo3 = new SimpleFoo();
     SimpleFoo foo4 = new SimpleFoo();
     SimpleFoo foo5 = new SimpleFoo();
-
+
     foo0.setSelfOneToManyField(Arrays.asList(foo1, foo2));
     foo0.setFooField(foo1);
     foo1.setFooField(foo2);
@@ -140,17 +140,17 @@
     foo3.setFooField(foo4);
     foo4.setFooField(foo5);
     foo5.setFooField(foo5);
-
+
     foo0.persist();
     foo1.persist();
     foo2.persist();
     foo3.persist();
     foo4.persist();
     foo5.persist();
-
+
     return foo0;
   }
-
+
   public static SimpleFoo getNullInEntityList() {
     SimpleFoo foo0 = new SimpleFoo();
     SimpleFoo foo1 = new SimpleFoo();
@@ -164,7 +164,7 @@
     foo2.setSelfOneToManyField(Arrays.asList(foo0));
     foo2.setFooField(foo2FooField);
     foo2FooField.setFooField(foo0);
-
+
     foo0.persist();
     foo1.persist();
     foo2.persist();
@@ -516,6 +516,14 @@
   private SimpleValue simpleValueField;
   private List<SimpleValue> simpleValuesField;

+  private Map<String,Integer> valueMap;
+
+  private Map<SimpleBar,Integer> simpleBarKeyMap;
+
+  private Map<Integer,SimpleBar> simpleBarValueMap;
+
+  private Map<MapKey,MapValue> entityKeyAndValueMap;;
+
   /*
    * isChanged is just a quick-and-dirty way to get version-ing for now.
    * Currently, only set by setUserName and setIntId. TODO for later: Use a
@@ -548,6 +556,16 @@
     barNullField = null;
     pleaseCrash = 0;
     isChanged = false;
+    valueMap = new HashMap<String, Integer>();
+    valueMap.put("foo", 3);
+    valueMap.put("bar", 7);
+    valueMap.put("baz", null);
+    simpleBarKeyMap = new HashMap<SimpleBar, Integer>();
+    simpleBarKeyMap.put(barField, 5);
+    simpleBarValueMap = new HashMap<Integer,SimpleBar>();
+    simpleBarValueMap.put(141, barField);
+    entityKeyAndValueMap = new HashMap<MapKey,MapValue>();
+ entityKeyAndValueMap.put(MapKey.getSingleton(), MapValue.getSingleton());
   }

   public Long countSimpleFooWithUserNameSideEffect() {
@@ -703,6 +721,22 @@

   public Integer getVersion() {
     return unpersisted ? null : version;
+  }
+
+  public Map<SimpleBar, Integer> getSimpleBarKeyMap() {
+    return simpleBarKeyMap;
+  }
+
+  public Map<Integer, SimpleBar> getSimpleBarValueMap() {
+    return simpleBarValueMap;
+  }
+
+  public Map<MapKey,MapValue> getEntityKeyAndValueMap() {
+    return entityKeyAndValueMap;
+  }
+
+  public Map<String, Integer> getValueMap() {
+    return valueMap;
   }

   public String hello(SimpleBar bar) {
@@ -882,6 +916,22 @@
     this.unpersisted = unpersisted;
   }

+  public void setValueMap(Map<String, Integer> valueMap) {
+    this.valueMap = valueMap;
+  }
+
+  public void setSimpleBarKeyMap(Map<SimpleBar, Integer> simpleValueMap) {
+    this.simpleBarKeyMap = simpleValueMap;
+  }
+
+ public void setSimpleBarValueMap(Map<Integer, SimpleBar> simpleBarValueMap) {
+    this.simpleBarValueMap = simpleBarValueMap;
+  }
+
+ public void setEntityKeyAndValueMap(Map<MapKey,MapValue> entityKeyAndValueMap) {
+    this.entityKeyAndValueMap = entityKeyAndValueMap;
+  }
+
   public void setUserName(String userName) {
     if (!this.userName.equals(userName)) {
       this.userName = userName;
@@ -903,7 +953,7 @@

   /**
* Persist this entity and all child entities. This method can handle loops.
-   *
+   *
    * @param processed the entities that have been processed
    */
private void persistCascadingAndReturnSelfImpl(Set<SimpleFoo> processed) {
@@ -951,4 +1001,5 @@
       }
     }
   }
+
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/BaseFooProxy.java b/user/test/com/google/web/bindery/requestfactory/shared/BaseFooProxy.java
index ca750ec..c1ee27c 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/BaseFooProxy.java +++ b/user/test/com/google/web/bindery/requestfactory/shared/BaseFooProxy.java
@@ -19,6 +19,7 @@
 import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;

 /**
@@ -75,6 +76,14 @@

   List<SimpleValueProxy> getSimpleValues();

+  Map<String,Integer> getValueMap();
+
+  Map<Integer,SimpleBarProxy> getSimpleBarValueMap();
+
+  Map<SimpleBarProxy,Integer> getSimpleBarKeyMap();
+
+  Map<MapKeyProxy,MapValueProxy> getEntityKeyAndValueMap();
+
   boolean getUnpersisted();

   String getUserName();
@@ -130,4 +139,13 @@
   void setUnpersisted(boolean unpersisted);

   void setUserName(String userName);
+
+  void setValueMap(Map<String,Integer> map);
+
+  void setSimpleBarKeyMap(Map<SimpleBarProxy,Integer> map);
+
+  void setSimpleBarValueMap(Map<Integer,SimpleBarProxy> map);
+
+  void setEntityKeyAndValueMap(Map<MapKeyProxy,MapValueProxy> map);
+
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/MapKeyProxy.java b/user/test/com/google/web/bindery/requestfactory/shared/MapKeyProxy.java
new file mode 100644
index 0000000..a90aa6b
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/shared/MapKeyProxy.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.shared;
+
+@ProxyForName("com.google.web.bindery.requestfactory.server.MapKey")
+public interface MapKeyProxy extends EntityProxy {
+
+  void setId(String id);
+
+  String getId();
+
+  void setSimple(SimpleValueProxy simple);
+
+  SimpleValueProxy getSimple();
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/MapKeyRequest.java b/user/test/com/google/web/bindery/requestfactory/shared/MapKeyRequest.java
new file mode 100644
index 0000000..76acaa3
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/shared/MapKeyRequest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.shared;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Do nothing test interface.
+ */
+@ServiceName("com.google.web.bindery.requestfactory.server.MapKey")
+public interface MapKeyRequest extends RequestContext {
+
+  Request<MapKeyProxy> findMapKeyById(String id);
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/MapValueProxy.java b/user/test/com/google/web/bindery/requestfactory/shared/MapValueProxy.java
new file mode 100644
index 0000000..93cfb9b
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/shared/MapValueProxy.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.shared;
+
+@ProxyForName("com.google.web.bindery.requestfactory.server.MapValue")
+public interface MapValueProxy extends EntityProxy {
+
+  void setId(String id);
+
+  String getId();
+
+  void setSimple(SimpleBarProxy simple);
+
+  SimpleBarProxy getSimple();
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/MapValueRequest.java b/user/test/com/google/web/bindery/requestfactory/shared/MapValueRequest.java
new file mode 100644
index 0000000..023d0fd
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/shared/MapValueRequest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 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.web.bindery.requestfactory.shared;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Do nothing test interface.
+ */
+@ServiceName("com.google.web.bindery.requestfactory.server.MapValue")
+public interface MapValueRequest extends RequestContext {
+
+  Request<MapValueProxy> findMapValueById(String id);
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/SimpleBarProxy.java b/user/test/com/google/web/bindery/requestfactory/shared/SimpleBarProxy.java
index 2995e06..9335422 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/SimpleBarProxy.java +++ b/user/test/com/google/web/bindery/requestfactory/shared/SimpleBarProxy.java
@@ -21,6 +21,9 @@
  */
 @ProxyForName("com.google.web.bindery.requestfactory.server.SimpleBar")
 public interface SimpleBarProxy extends EntityProxy {
+
+//  SimpleValueProxy getSimpleValue();
+
   Boolean getFindFails();

   Boolean getUnpersisted();
@@ -38,5 +41,7 @@

   void setUserName(String userName);

+//  void setSimpleValue(SimpleValueProxy value);
+
   EntityProxyId<SimpleBarProxy> stableId();
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java b/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
index 74f4b0d..f2cf7c8 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java +++ b/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
@@ -31,4 +31,8 @@
   SimpleValueContext simpleValueContext();

   UnicodeTestRequest unicodeTestRequest();
+
+  MapKeyRequest mapKeyRequest();
+
+  MapValueRequest mapValueRequest();
 }

--
To view, visit https://gwt-review.googlesource.com/3186
To unsubscribe, visit https://gwt-review.googlesource.com/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6dd3c8a9862473954a72ccee96212c20d9198b22
Gerrit-PatchSet: 1
Gerrit-Project: gwt
Gerrit-Branch: master
Gerrit-Owner: James Horsley <james.hors...@gmail.com>

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors
--- You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit-contributors+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to