+ public ActionCodecContext load(final Class<? extends Action<?, ?, ?>> action) {
+ if (KeyedListAction.class.isAssignableFrom(action)) {
+ return prepareActionContext(2, 3, 4, action, KeyedListAction.class);
+ } else if (Action.class.isAssignableFrom(action)) {
+ return prepareActionContext(1, 2, 3, action, Action.class);
+ }
+ throw new IllegalArgumentException("The specific action type does not exist for action "
+ + action.getName());
+ }
+
+ private ActionCodecContext prepareActionContext(final int inputOffset, final int outputOffset,
+ final int expectedArgsLength, final Class<? extends Action<?, ?, ?>> action,
+ final Class<?> actionType) {
+ final var args = ClassLoaderUtils.findParameterizedType(action, actionType)
+ .orElseThrow(() -> new IllegalStateException(action + " does not specialize " + actionType))
+ .getActualTypeArguments();
+ checkArgument(args.length == expectedArgsLength, "Unexpected (%s) Action generatic arguments",
+ args.length);
+ final ActionRuntimeType schema = context.getActionDefinition(action);
+ return new ActionCodecContext(
+ new ContainerLikeCodecContext(asClass(args[inputOffset], RpcInput.class), schema.input(),
+ BindingCodecContext.this),
+ new ContainerLikeCodecContext(asClass(args[outputOffset], RpcOutput.class), schema.output(),
+ BindingCodecContext.this));
+ }
+
+ private static <T extends DataObject> Class<? extends T> asClass(final Type type, final Class<T> target) {
+ verify(type instanceof Class, "Type %s is not a class", type);
+ return ((Class<?>) type).asSubclass(target);
+ }
+ });
+
+ private final LoadingCache<Class<?>, NotificationCodecContext<?>> notificationsByClass = CacheBuilder.newBuilder()
+ .build(new CacheLoader<>() {
+ @Override
+ public NotificationCodecContext<?> load(final Class<?> key) {
+ final var runtimeType = context.getTypes().bindingChild(JavaTypeName.create(key));
+ if (runtimeType instanceof NotificationRuntimeType notification) {
+ return new NotificationCodecContext<>(key, notification, BindingCodecContext.this);
+ } if (runtimeType != null) {
+ throw new IllegalArgumentException(key + " maps to unexpected " + runtimeType);
+ }
+ throw new IllegalArgumentException(key + " is not a known class");
+ }
+ });
+ private final LoadingCache<Absolute, NotificationCodecContext<?>> notificationsByPath =
+ CacheBuilder.newBuilder().build(new CacheLoader<>() {
+ @Override
+ public NotificationCodecContext<?> load(final Absolute key) {
+ final var cls = context.getClassForSchema(key);
+ try {
+ return getNotificationContext(cls.asSubclass(Notification.class));
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Path " + key + " does not represent a notification", e);
+ }
+ }
+ });
+
+ private final LoadingCache<Class<?>, ContainerLikeCodecContext<?>> rpcDataByClass =
+ CacheBuilder.newBuilder().build(new CacheLoader<>() {
+ @Override
+ public ContainerLikeCodecContext<?> load(final Class<?> key) {
+ final var runtimeType = context.getTypes().findSchema(JavaTypeName.create(key))
+ .orElseThrow(() -> new IllegalArgumentException(key + " is not a known class"));
+ if (RpcInput.class.isAssignableFrom(key) && runtimeType instanceof InputRuntimeType input) {
+ // FIXME: accurate type
+ return new ContainerLikeCodecContext(key, input, BindingCodecContext.this);
+ } else if (RpcOutput.class.isAssignableFrom(key) && runtimeType instanceof OutputRuntimeType output) {
+ // FIXME: accurate type
+ return new ContainerLikeCodecContext(key, output, BindingCodecContext.this);
+ } else {
+ throw new IllegalArgumentException(key + " maps to unexpected " + runtimeType);
+ }