All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avsystem.commons.serialization.InputOutput.scala Maven / Gradle / Ivy

The newest version!
package com.avsystem.commons
package serialization

/**
  * Represents an abstract sink to which a value may be serialized (written).
  * An [[Output]] instance should be assumed to be stateful. After calling any of the `write` methods, it MUST NOT be
  * reused. This means that [[Output]] instance can be used only to write a single value. However, if the value
  * to write is complex, one can use `writeList`/`writeSet` or `writeObject`/`writeMap`.
  */
trait Output extends Any {
  def writeNull(): Unit
  def writeUnit(): Unit = writeNull()
  def writeString(str: String): Unit
  def writeChar(char: Char): Unit = writeString(char.toString)
  def writeBoolean(boolean: Boolean): Unit
  def writeByte(byte: Byte): Unit = writeShort(byte)
  def writeShort(short: Short): Unit = writeInt(short)
  def writeInt(int: Int): Unit
  def writeLong(long: Long): Unit
  def writeTimestamp(millis: Long): Unit = writeLong(millis)
  def writeFloat(float: Float): Unit = writeDouble(float)
  def writeDouble(double: Double): Unit
  def writeBinary(binary: Array[Byte]): Unit
  def writeList(): ListOutput
  def writeSet(): ListOutput = writeList()
  def writeObject(): ObjectOutput
  def writeMap(): ObjectOutput = writeObject()
}
/**
  * Base trait for outputs which allow writing of multiple values in sequence, i.e. [[ListOutput]] and [[ObjectOutput]].
  */
trait SequentialOutput extends Any {
  /**
    * Indicates that all elements or fields in this [[SequentialOutput]] have been written. This method MUST always
    * be called after list/object writing has been finished.
    */
  def finish(): Unit
}
/**
  * Represents an abstract sink for serialization of sequences of values. Any [[ListOutput]] instance
  * must be assumed to be stateful and used in strictly sequential manner. After all elements have been written,
  * `finish()` must be called to explicitly mark that the list is complete.
  */
trait ListOutput extends SequentialOutput {
  /**
    * Returns an [[Output]] representing next element in this list. This [[Output]] instance MUST be fully used
    * before calling [[writeElement]] next time. That means, one can NOT simultaneously use multiple instances of
    * [[Output]] returned by subsequent calls to this method.
    */
  def writeElement(): Output
}
/**
  * Represents an abstract sink for serialization of string-to-value mappings. Any [[ObjectOutput]] instance
  * must be assumed to be stateful and used in strictly sequential manner. After all key-value pairs have been
  * written, `finish()` must be called to explicitly mark that the object is complete.
  */
trait ObjectOutput extends SequentialOutput {
  /**
    * Returns an [[Output]] representing value mapped to given string key. This [[Output]] instance must be fully
    * used before calling [[writeField]] next time. That means, one can NOT simultaneously use multiple instances
    * of [[Output]] returned by subsequent calls to this method.
    */
  def writeField(key: String): Output
}

/**
  * Represents the type of value inside and [[Input]] that can be read from it.
  * 

* It is possible to distinguish only between four types (null, simple value, object and list) even though * any of these types may have different representations. For example, [[InputType.Simple]] is returned for * more than one actual value types (numbers, booleans, strings, timestamps, binary, etc.). *

* It's not possible to distinguish between them based only on [[InputType]] because not every [[Input]] * implementation is able to do that. For example, JSON must represent 64-bit integers as strings and therefore * it can't distinguish between strings and numbers in general. */ sealed trait InputType object InputType { case object Null extends InputType case object Simple extends InputType case object Object extends InputType case object List extends InputType } /** * Represents an abstract source from which a value may be deserialized (read). * Each of the `read` methods tries to read a value of specified type and may throw an exception * (usually [[com.avsystem.commons.serialization.GenCodec.ReadFailure]]) when reading is not successful. *

* An [[Input]] value should be assumed to be stateful. If any of the `readX` methods have already been called, * the [[Input]] instance can no longer be used and MUST be discarded. *

* In order to ignore the value kept in this [[Input]], `skip()` MUST be called. *

* In summary: every [[Input]] MUST be fully exhausted by either calling one of the `read` methods which returns * successful value or by calling `skip()`. Also, [[ListInput]] and [[ObjectInput]] instances returned from this * [[Input]] must also be fully exhausted on their own. */ trait Input extends Any { /** * Returns the type of the value that can be read from this [[Input]]. * Only four types can be distinguished (see [[InputType]] for more details on this). *

* If this method returns [[InputType.Null]], then `readNull()` can be safely called.
* If this method returns [[InputType.Object]], then AT LEAST ONE OF `readObject()` and `readMap()` can be safely called.
* If this method returns [[InputType.List]], then AT LEAST ONE OF `readList()` and `readSet()` can be safely called.
* If this method returns [[InputType.Simple]] then AT LEAST ONE OF `readString()`, `readChar()`, `readBoolean()`, * `readByte()`, `readShort()`, `readInt()`, `readLong()`, `readTimestamp()`, `readFloat()`, `readDouble()`, * `readBinary()` can be called. *

* It's impossible to know which of the listed methods is actually safe to call based only on [[InputType]]. * It is the responsibility of [[GenCodec]] implementation to have reading and writing logic consistent. * For example, if `writeDouble(Double)` is used during writing then `readDouble()` must be used during reading * by the same [[GenCodec]]. * * @return */ def inputType: InputType def readNull(): Null def readUnit(): Unit = readNull() def readString(): String def readChar(): Char = readString().charAt(0) def readBoolean(): Boolean def readByte(): Byte = readShort().toByte def readShort(): Short = readInt().toShort def readInt(): Int def readLong(): Long def readTimestamp(): Long = readLong() def readFloat(): Float = readDouble().toFloat def readDouble(): Double def readBinary(): Array[Byte] def readList(): ListInput def readSet(): ListInput = readList() def readObject(): ObjectInput def readMap(): ObjectInput = readObject() def skip(): Unit } trait SequentialInput extends Any { def hasNext: Boolean def skipRemaining(): Unit } /** * Represents an abstract source of sequence of values that can be deserialized. * [[ListInput]] instance is stateful and MUST be read strictly sequentially. This means, you MUST fully exhaust * an [[Input]] instance returned by `nextElement()` before calling `nextElement()` again. For this reason, * [[ListInput]] is not an `Iterator` despite having similar interface * (`Iterator` would easily allow e.g. conversion to `List[Input]` which would be illegal). *

* [[ListInput]] MUST always be fully exhausted. In order to ignore any remaining elements, skipRemaining() may be * used. */ trait ListInput extends SequentialInput { self => /** * Returns an [[Input]] representing next element in a sequence of values represented by this [[ListInput]]. * Returned [[Input]] instance must be fully exhausted before calling `nextElement()` next time. */ def nextElement(): Input def skipRemaining() = while (hasNext) nextElement().skip() def iterator[A](readFun: Input => A): Iterator[A] = new Iterator[A] { def hasNext = self.hasNext def next() = readFun(nextElement()) } } /** * Represents an abstract source of key-value mappings that can be deserialized. * [[ObjectInput]] instance is stateful and MUST be read strictly sequentially. This means, you MUST fully exhaust * any [[Input]] instance returned by `nextField()` before calling `nextField()` again. For this reason, * [[ObjectInput]] is not an `Iterator` despite having similar interface * (`Iterator` would easily allow e.g. conversion to `List[(String, Input)]` which would be illegal). *

* [[ObjectInput]] MUST always be fully exhausted. In order to ignore any remaining key-value mappings, * `skipRemaining()` may be used. *

* NOTE: The order of keys returned by subsequent invocations of `nextField()` may be arbitrary and in particular * may not match the order in which keys were written to corresponding [[ObjectOutput]]. */ trait ObjectInput extends SequentialInput { self => def nextField(): FieldInput def skipRemaining() = while (hasNext) nextField().skip() def iterator[A](readFun: Input => A): Iterator[(String, A)] = new Iterator[(String, A)] { def hasNext = self.hasNext def next() = { val fi = nextField() (fi.fieldName, readFun(fi)) } } } /** * An [[Input]] representing an object field. The same as [[Input]] but also provides field name. */ trait FieldInput extends Input { def fieldName: String }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy