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

wvlet.airframe.surface.Zero.scala Maven / Gradle / Ivy

There is a newer version: 24.11.0
Show newest version
/*
 * 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 wvlet.airframe.surface

import java.util.concurrent.ConcurrentHashMap

import wvlet.log.LogSupport

import scala.reflect.ClassTag

/**
  * Create a default instance (zero) from Surface
  */
object Zero extends LogSupport {
  import scala.jdk.CollectionConverters.*
  private val preregisteredZeroInstance = new ConcurrentHashMap[Surface, Any]().asScala

  /**
    * Register a zero instance for the given type
    */
  def register(surface: Surface, zero: Any): Unit = {
    preregisteredZeroInstance += surface -> zero
  }

  type ZeroValueFactory = PartialFunction[Surface, Any]
  type SurfaceFilter    = PartialFunction[Surface, Surface]

  private def isPrimitive: SurfaceFilter = {
    case p if p.isPrimitive => p
  }
  private def isGenericWithTypeArgs: SurfaceFilter = {
    case g: GenericSurface if g.typeArgs.length > 0 => g
  }

  private def zeroOfPrimitives: ZeroValueFactory =
    isPrimitive andThen {
      case Primitive.String     => ""
      case Primitive.Boolean    => false
      case Primitive.Int        => 0
      case Primitive.Long       => 0L
      case Primitive.Float      => 0f
      case Primitive.Double     => 0.0
      case Primitive.Unit       => null
      case Primitive.Byte       => 0.toByte
      case Primitive.Short      => 0.toShort
      case Primitive.Char       => 0.toChar
      case Primitive.BigInt     => BigInt(0)
      case Primitive.BigInteger => java.math.BigInteger.ZERO
    }

  private def zeroOfRegisteredTypes: ZeroValueFactory = {
    case t if preregisteredZeroInstance.contains(t) =>
      preregisteredZeroInstance(t)
  }

  private def zeroOfArray: ZeroValueFactory = { case ArraySurface(cl, elementSurface) =>
    ClassTag(elementSurface.rawType).newArray(0)
  }

  private def zeroOfSpecialType: ZeroValueFactory = {
    case s if s.isOption => None
    case s
        if s.rawType == classOf[Nothing] ||
          s.rawType == classOf[AnyRef] ||
          s.rawType == classOf[Any] =>
      null
  }

  private def zeroOfScalaCollections: ZeroValueFactory =
    isGenericWithTypeArgs andThen {
      case g if classOf[List[_]].isAssignableFrom(g.rawType) =>
        List.empty
      case g if classOf[Seq[_]].isAssignableFrom(g.rawType) =>
        Seq.empty
      case g if classOf[Map[_, _]].isAssignableFrom(g.rawType) =>
        Map.empty
      case g if classOf[Set[_]].isAssignableFrom(g.rawType) =>
        Set.empty
    }

  private def zeroOfTuple: ZeroValueFactory = { case t: TupleSurface =>
    val args = t.typeArgs.map(s => zeroOf(s)).toIndexedSeq
    t.typeArgs.size match {
      case 1 => ()
      case 2 => (args(0), args(1))
      case 3 => (args(0), args(1), args(2))
      case 4 => (args(0), args(1), args(2), args(3))
      case 5 => (args(0), args(1), args(2), args(3), args(4))
      case 6 => (args(0), args(1), args(2), args(3), args(4), args(5))
      case 7 => (args(0), args(1), args(2), args(3), args(4), args(5), args(6))
      case other =>
        new UnsupportedOperationException(s"Zero.of[Tuple${other}] is not supported")
    }
  }

  private def zeroOfInstantiatable: ZeroValueFactory = {
    case t if t.objectFactory.isDefined =>
      val factory = t.objectFactory.get
      val args    = t.params.map(x => x.getDefaultValue.getOrElse(zeroOf(x.surface)))
      factory.newInstance(args)
  }

  private def fallBack: ZeroValueFactory = { case _ =>
    null
  }

  private val factory: ZeroValueFactory =
    zeroOfPrimitives orElse
      zeroOfRegisteredTypes orElse
      zeroOfArray orElse
      zeroOfTuple orElse
      zeroOfSpecialType orElse
      zeroOfScalaCollections orElse
      zeroOfInstantiatable orElse
      fallBack

  def zeroOf(surface: Surface): Any = {
    // Dealias function resolves the actual types of aliased surfaces, tagged surfaces etc.
    val zero = factory.apply(surface.dealias)
    zero
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy