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

kinesis4cats.compat.OptionConverters.scala Maven / Gradle / Ivy

/*
 * Copyright 2023-2023 etspaceman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kinesis4cats.compat

import java.util.{Optional, OptionalDouble, OptionalInt, OptionalLong}

/** This class enables bidirectional conversion between `scala.Option` and the
  * set of `java.util.Optional` classes.
  *
  * The Scala `Option` is generic; its generic counterpart in Java is
  * `java.util.Optional`. `Option` is enriched with an `asJava` method, while
  * `Optional` is enriched with `asScala` to perform conversions.
  *
  * In addition, both `Option` and `Optional` are enriched with `asPrimitive`
  * methods that will convert generically contained primitives to the manually
  * specialized Java versions for primitives, `OptionalDouble`, `OptionalInt`,
  * and `OptionalLong`. The primitive versions can be converted to the Scala
  * generic `Option` with `asScala` and to the Java generic `Optional` with
  * `asGeneric`.
  *
  * When calling from Java, methods are more convenient than extension methods,
  * so `toJava` and `toScala` methods are provided that convert to and from
  * Scala's `Option`. Note that `toJava(toScala(x))` will result in a generic
  * `Optional` even if `x` was one of the primitive versons.
  *
  * Example usage:
  *
  * {{{
  * import scala.compat.java8.OptionConverters._
  * val a = Option("example").asJava      // Creates java.util.Optional[String] containing "example"
  * val b = (None: Option[String]).asJava // Creates an empty java.util.Optional[String]
  * val c = a.asScala                     // Back to Option("example")
  * val d = b.asScala                     // Back to None typed as Option[String]
  * val e = Option(2.7).asJava            // java.util.Optional[Double] containing boxed 2.7
  * val f = Option(2.7).asPrimitive       // java.util.OptionalDouble containing 2.7 (not boxed)
  * val g = f.asScala                     // Back to Option(2.7)
  * val h = f.asGeneric                   // Same as e
  * val i = e.asPrimitive                 // Same as f
  * val j = toJava(Option("example"))     // Same as a
  * val k = toScala(a)                    // Same as c
  * }}}
  */
object OptionConverters {

  /** Type class implementing conversion from generic `Option` or `Optional` to
    * manually specialized variants.
    */
  sealed abstract class SpecializerOfOptions[A, That] {

    /** Converts from `Optional` to a manually specialized variant `That` */
    def fromJava(o: Optional[A]): That

    /** Converts from `Option` to a manually specialized variant `That` */
    def fromScala(o: Option[A]): That
  }

  /** Implementation of creation of `OptionalDouble` from `Option[Double]` or
    * `Optional[Double]`
    */
  implicit val specializer_OptionalDouble
      : SpecializerOfOptions[Double, OptionalDouble] =
    new SpecializerOfOptions[Double, OptionalDouble] {

      /** Creates an `OptionalDouble` from `Optional[Double]` */
      def fromJava(o: Optional[Double]): OptionalDouble =
        if (o.isPresent) OptionalDouble.of(o.get) else OptionalDouble.empty

      /** Creates an `OptionalDouble` from `Option[Double]` */
      def fromScala(o: Option[Double]): OptionalDouble = o match {
        case Some(d) => OptionalDouble.of(d); case _ => OptionalDouble.empty
      }
    }

  /** Implementation of creation of `OptionalInt` from `Option[Int]` or
    * `Optional[Int]`
    */
  implicit val specializer_OptionalInt: SpecializerOfOptions[Int, OptionalInt] =
    new SpecializerOfOptions[Int, OptionalInt] {

      /** Creates an `OptionalInt` from `Optional[Int]` */
      def fromJava(o: Optional[Int]): OptionalInt =
        if (o.isPresent) OptionalInt.of(o.get) else OptionalInt.empty

      /** Creates an `OptionalInt` from `Option[Int]` */
      def fromScala(o: Option[Int]): OptionalInt = o match {
        case Some(d) => OptionalInt.of(d); case _ => OptionalInt.empty
      }
    }

  /** Implementation of creation of `OptionalLong` from `Option[Long]` or
    * `Optional[Long]`
    */
  implicit val specializer_OptionalLong
      : SpecializerOfOptions[Long, OptionalLong] =
    new SpecializerOfOptions[Long, OptionalLong] {

      /** Creates an `OptionalLong` from `Optional[Long]` */
      def fromJava(o: Optional[Long]): OptionalLong =
        if (o.isPresent) OptionalLong.of(o.get) else OptionalLong.empty

      /** Creates an `OptionalLong` from `Option[Long]` */
      def fromScala(o: Option[Long]): OptionalLong = o match {
        case Some(d) => OptionalLong.of(d); case _ => OptionalLong.empty
      }
    }

  /** Provides conversions from `java.util.Optional` to Scala `Option` or
    * primitive `java.util.Optional` types
    */
  implicit class RichOptionalGeneric[A](val underlying: java.util.Optional[A])
      extends AnyVal {

    /** Create a `scala.Option` version of this `Optional` */
    def asScala: Option[A] =
      if (underlying.isPresent) Some(underlying.get) else None

    /** Create a specialized primitive variant of this generic `Optional`, if an
      * appropriate one exists
      */
    def asPrimitive[That](implicit
        specOp: SpecializerOfOptions[A, That]
    ): That = specOp.fromJava(underlying)
  }

  /** Provides conversions from `scala.Option` to Java `Optional` types, either
    * generic or primitive
    */
  implicit class RichOptionForJava8[A](val underlying: Option[A])
      extends AnyVal {

    /** Create a `java.util.Optional` version of this `Option` (not specialized)
      */
    def asJava: Optional[A] = underlying match {
      case Some(a) => Optional.ofNullable(a); case _ => Optional.empty[A]
    }

    /** Create a specialized primitive `java.util.Optional` type, if an
      * appropriate one exists
      */
    def asPrimitive[That](implicit
        specOp: SpecializerOfOptions[A, That]
    ): That = specOp.fromScala(underlying)
  }

  /** Provides conversions from `java.util.OptionalDouble` to the generic
    * `Optional` and Scala `Option`
    */
  implicit class RichOptionalDouble(val underlying: OptionalDouble)
      extends AnyVal {

    /** Create a `scala.Option` version of this `OptionalDouble` */
    def asScala: Option[Double] =
      if (underlying.isPresent) Some(underlying.getAsDouble) else None

    /** Create a generic `java.util.Optional` version of this `OptionalDouble`
      */
    def asGeneric: Optional[Double] = if (underlying.isPresent)
      Optional.of(underlying.getAsDouble)
    else Optional.empty[Double]
  }

  /** Provides conversions from `java.util.OptionalInt` to the generic
    * `Optional` and Scala `Option`
    */
  implicit class RichOptionalInt(val underlying: OptionalInt) extends AnyVal {

    /** Create a `scala.Option` version of this `OptionalInt` */
    def asScala: Option[Int] =
      if (underlying.isPresent) Some(underlying.getAsInt) else None

    /** Create a generic `java.util.Optional` version of this `OptionalInt` */
    def asGeneric: Optional[Int] = if (underlying.isPresent)
      Optional.of(underlying.getAsInt)
    else Optional.empty[Int]
  }

  /** Provides conversions from `java.util.OptionalLong` to the generic
    * `Optional` and Scala `Option`
    */
  implicit class RichOptionalLong(val underlying: OptionalLong) extends AnyVal {

    /** Create a `scala.Option` version of this `OptionalLong` */
    def asScala: Option[Long] =
      if (underlying.isPresent) Some(underlying.getAsLong) else None

    /** Create a generic `java.util.Optional` version of this `OptionalLong` */
    def asGeneric: Optional[Long] = if (underlying.isPresent)
      Optional.of(underlying.getAsLong)
    else Optional.empty[Long]
  }

  /** Conversion from Scala `Option` to Java `Optional` without using implicits,
    * for convenient use from Java.
    */
  final def toJava[A](o: Option[A]): Optional[A] = o match {
    case Some(a) => Optional.ofNullable(a); case _ => Optional.empty[A]
  }

  /** Conversion from Java `Optional` to Scala `Option` without using implicits,
    * for convenient use from Java
    */
  final def toScala[A](o: Optional[A]): Option[A] =
    if (o.isPresent) Some(o.get) else None

  /** Conversion from Java `OptionalDouble` to Scala `Option` without using
    * implicits, for convenient use from Java
    */
  final def toScala(o: OptionalDouble): Option[Double] =
    if (o.isPresent) Some(o.getAsDouble) else None

  /** Conversion from Java `OptionalInt` to Scala `Option` without using
    * implicits, for convenient use from Java
    */
  final def toScala(o: OptionalInt): Option[Int] =
    if (o.isPresent) Some(o.getAsInt) else None

  /** Conversion from Java `OptionalLong` to Scala `Option` without using
    * implicits, for convenient use from Java
    */
  final def toScala(o: OptionalLong): Option[Long] =
    if (o.isPresent) Some(o.getAsLong) else None
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy