/**
   * Creates a Resolution object that holds a client value that represents the given domain value.
   * The resolved client value will be assignable to {@code clientType}.
   */
  private Resolution resolveClientValue(Object domainValue, Type clientType) {
    if (domainValue == null) {
      return new Resolution(null);
    }

    boolean anyType = clientType == null;
    if (anyType) {
      clientType = Object.class;
    }

    Class<?> assignableTo = TypeUtils.ensureBaseType(clientType);
    ResolutionKey key = new ResolutionKey(domainValue, clientType);

    Resolution previous = resolved.get(key);
    if (previous != null && assignableTo.isInstance(previous.getClientObject())) {
      return previous;
    }

    Class<?> returnClass = service.resolveClientType(domainValue.getClass(), assignableTo, true);

    if (anyType) {
      assignableTo = returnClass;
    }

    // Pass simple values through
    if (ValueCodex.canDecode(returnClass)) {
      return makeResolution(domainValue);
    }

    // Convert entities to EntityProxies or EntityProxyIds
    boolean isProxy = BaseProxy.class.isAssignableFrom(returnClass);
    boolean isId = EntityProxyId.class.isAssignableFrom(returnClass);
    if (isProxy || isId) {
      Class<? extends BaseProxy> proxyClass = returnClass.asSubclass(BaseProxy.class);
      return resolveClientProxy(domainValue, proxyClass, key);
    }

    // Convert collections
    if (Collection.class.isAssignableFrom(returnClass)) {
      Collection<Object> accumulator;
      if (List.class.isAssignableFrom(returnClass)) {
        accumulator = new ArrayList<Object>();
      } else if (Set.class.isAssignableFrom(returnClass)) {
        accumulator = new HashSet<Object>();
      } else {
        throw new ReportableException("Unsupported collection type" + returnClass.getName());
      }

      Type elementType = TypeUtils.getSingleParameterization(Collection.class, clientType);
      for (Object o : (Collection<?>) domainValue) {
        accumulator.add(resolveClientValue(o, elementType).getClientObject());
      }
      return makeResolution(accumulator);
    }

    throw new ReportableException("Unsupported domain type " + returnClass.getCanonicalName());
  }
    @Override
    public boolean visitReferenceProperty(
        String propertyName, AutoBean<?> value, PropertyContext ctx) {
      /*
       * Send the property if the enclosing type is a ValueProxy, if the owner
       * requested the property, or if the property is a list of values.
       */
      Class<?> elementType =
          ctx instanceof CollectionPropertyContext
              ? ((CollectionPropertyContext) ctx).getElementType()
              : null;
      boolean shouldSend =
          isOwnerValueProxy
              || matchesPropertyRef(propertyRefs, propertyName)
              || elementType != null && ValueCodex.canDecode(elementType);

      if (!shouldSend) {
        return false;
      }

      // Call the getter
      Object domainValue = service.getProperty(domainEntity, propertyName);
      if (domainValue == null) {
        return false;
      }

      // Turn the domain object into something usable on the client side
      Type type;
      if (elementType == null) {
        type = ctx.getType();
      } else {
        type = new CollectionType(ctx.getType(), elementType);
      }
      Resolution resolution = resolveClientValue(domainValue, type);
      addPathsToResolution(resolution, propertyName, propertyRefs);
      ctx.set(resolution.getClientObject());
      return false;
    }