/** * 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()); }
/** Creates a proxy instance held by a Resolution for a given domain type. */ private <T extends BaseProxy> Resolution resolveClientProxy( Object domainEntity, Class<T> proxyType, ResolutionKey key) { if (domainEntity == null) { return null; } SimpleProxyId<? extends BaseProxy> id = state.getStableId(domainEntity); boolean isEntityProxy = state.isEntityType(proxyType); Object domainVersion; // Create the id or update an ephemeral id by calculating its address if (id == null || id.isEphemeral()) { // The address is an id or an id plus a path Object domainId; if (isEntityProxy) { // Compute data needed to return id to the client domainId = service.getId(domainEntity); domainVersion = service.getVersion(domainEntity); } else { domainId = null; domainVersion = null; } if (id == null) { if (domainId == null) { /* * This will happen when server code attempts to return an unpersisted * object to the client. In this case, we'll assign a synthetic id * that is valid for the duration of the response. The client is * expected to assign a client-local id to this object and then it * will behave as though it were an object newly-created by the * client. */ id = state.getIdFactory().allocateSyntheticId(proxyType, ++syntheticId); } else { Splittable flatValue = state.flatten(domainId); id = state.getIdFactory().getId(proxyType, flatValue.getPayload(), 0); } } else if (domainId != null) { // Mark an ephemeral id as having been persisted Splittable flatValue = state.flatten(domainId); id.setServerId(flatValue.getPayload()); } } else if (isEntityProxy) { // Already have the id, just pull the current version domainVersion = service.getVersion(domainEntity); } else { // The version of a value object is always null domainVersion = null; } @SuppressWarnings("unchecked") AutoBean<T> bean = (AutoBean<T>) state.getBeanForPayload(id, domainEntity); bean.setTag(Constants.IN_RESPONSE, true); if (domainVersion != null) { Splittable flatVersion = state.flatten(domainVersion); bean.setTag( Constants.VERSION_PROPERTY_B64, SimpleRequestProcessor.toBase64(flatVersion.getPayload())); } T clientObject = bean.as(); return makeResolution(key, clientObject); }