Class ChoiceCodec.Builder<T,V>

java.lang.Object
io.bytestreams.codec.core.ChoiceCodec.Builder<T,V>
Type Parameters:
T - the tag type
V - the base type of the discriminated union
Enclosing class:
ChoiceCodec<T,V>

public static class ChoiceCodec.Builder<T,V> extends Object
Builder for ChoiceCodec.
  • Method Details

    • on

      public <S extends V> ChoiceCodec.Builder<T,V> on(T tag, Class<S> type, Codec<S> codec)
      Registers an alternative.
      Type Parameters:
      S - the subtype
      Parameters:
      tag - the tag identifying this alternative on the wire
      type - the class of values for this alternative
      codec - the codec for values of this class
      Returns:
      this builder
      Throws:
      NullPointerException - if tag, type or codec is null
      IllegalArgumentException - if the tag or the type is already registered, or if the tag is an array
    • otherwise

      public <U extends V> ChoiceCodec.Builder<T,V> otherwise(Class<U> type, Function<U,T> tagOf, Function<T,Codec<U>> body)
      Registers a fallback for alternatives that are not registered, so unknown types round trip instead of failing.

      Three things are needed, and each is used in both directions: the class recognises an unknown value when encoding, tagOf supplies the tag to write for it, and body builds the codec for the bytes after the tag. Because the tag comes from the value rather than from the wire, nothing has to be rewound, so a choice codec never constrains the stream it is given.

      .otherwise(
          Unknown.class,
          Unknown::tag,
          tag -> Codecs.binary().xmap(body -> new Unknown(tag, body), Unknown::body))
      

      Declaring the class keeps encoding checked: a value that is neither registered nor an instance of this type raises a CodecException naming it, rather than reaching a codec that cannot handle it. A tagOf that returns a tag already registered is rejected too, since those bytes would read back as that other alternative.

      The tag is re-encoded from the decoded value rather than replayed, so byte-for-byte passthrough of an unknown alternative holds exactly when the tag codec round trips — as every tag codec in this library does.

      A body codec that reads to end-of-stream needs a bounded scope — inside Codecs.prefixed(Codec, Codec) or Codecs.terminated(Codec, byte[]), which hand down a bounded stream — since an unknown alternative has unknown extent.

      Type Parameters:
      U - the fallback subtype
      Parameters:
      type - the class of values this fallback produces and encodes
      tagOf - returns the tag to write for a value of that class
      body - returns the codec for the body following a given tag
      Returns:
      this builder
      Throws:
      NullPointerException - if any argument is null
      IllegalArgumentException - if a fallback is already registered, or if the type is already registered as an alternative
    • build

      public ChoiceCodec<T,V> build()
      Builds the choice codec.
      Returns:
      a new choice codec
      Throws:
      IllegalArgumentException - if no alternatives have been registered