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

com.twitter.bijection.CollectionBijections.scala Maven / Gradle / Ivy

The newest version!
/*
Copyright 2012 Twitter, Inc.

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 com.twitter.bijection

import java.lang.{Iterable => JIterable}
import java.util.{
  Collection => JCollection,
  Dictionary => JDictionary,
  Enumeration => JEnumeration,
  Iterator => JIterator,
  List => JList,
  Map => JMap,
  Set => JSet
}
import scala.jdk.CollectionConverters._
import scala.collection.mutable
import scala.collection.Factory
import scala.reflect.ClassTag

trait CollectionBijections extends BinaryBijections {

  /**
    * Bijections between collection types defined in scala.collection.JavaConverters.
    */
  implicit def iterable2java[T]: Bijection[Iterable[T], JIterable[T]] =
    new AbstractBijection[Iterable[T], JIterable[T]] {
      override def apply(t: Iterable[T]) = t.asJava
      override def invert(u: JIterable[T]) = u.asScala
    }

  implicit def iterator2java[T]: Bijection[Iterator[T], JIterator[T]] =
    new AbstractBijection[Iterator[T], JIterator[T]] {
      override def apply(t: Iterator[T]) = t.asJava
      override def invert(u: JIterator[T]) = u.asScala
    }
  implicit def buffer2java[T]: Bijection[mutable.Buffer[T], JList[T]] =
    new AbstractBijection[mutable.Buffer[T], JList[T]] {
      override def apply(t: mutable.Buffer[T]) = t.asJava
      override def invert(u: JList[T]) = u.asScala
    }
  implicit def mset2java[T]: Bijection[mutable.Set[T], JSet[T]] =
    new AbstractBijection[mutable.Set[T], JSet[T]] {
      override def apply(t: mutable.Set[T]) = t.asJava
      override def invert(u: JSet[T]) = u.asScala
    }
  implicit def mmap2java[K, V]: Bijection[mutable.Map[K, V], JMap[K, V]] =
    new AbstractBijection[mutable.Map[K, V], JMap[K, V]] {
      override def apply(t: mutable.Map[K, V]) = t.asJava
      override def invert(t: JMap[K, V]) = t.asScala
    }
  implicit def iterable2jcollection[T]: Bijection[Iterable[T], JCollection[T]] =
    new AbstractBijection[Iterable[T], JCollection[T]] {
      override def apply(t: Iterable[T]) = t.asJavaCollection
      override def invert(u: JCollection[T]) = u.asScala
    }
  implicit def iterator2jenumeration[T]: Bijection[Iterator[T], JEnumeration[T]] =
    new AbstractBijection[Iterator[T], JEnumeration[T]] {
      override def apply(t: Iterator[T]) = t.asJavaEnumeration
      override def invert(u: JEnumeration[T]) = u.asScala
    }
  implicit def mmap2jdictionary[K, V]: Bijection[mutable.Map[K, V], JDictionary[K, V]] =
    new AbstractBijection[mutable.Map[K, V], JDictionary[K, V]] {
      override def apply(t: mutable.Map[K, V]) = t.asJavaDictionary
      override def invert(t: JDictionary[K, V]) = t.asScala
    }
  // Immutable objects (they copy from java to scala):
  implicit def seq2Java[T]: Bijection[Seq[T], JList[T]] =
    new AbstractBijection[Seq[T], JList[T]] {
      def apply(s: Seq[T]) = s.asJava
      override def invert(l: JList[T]) = l.asScala.toSeq
    }
  implicit def set2Java[T]: Bijection[Set[T], JSet[T]] =
    new AbstractBijection[Set[T], JSet[T]] {
      def apply(s: Set[T]) = s.asJava
      override def invert(l: JSet[T]) = l.asScala.toSet
    }
  implicit def map2Java[K, V]: Bijection[Map[K, V], JMap[K, V]] =
    new AbstractBijection[Map[K, V], JMap[K, V]] {
      def apply(s: Map[K, V]) = s.asJava
      override def invert(l: JMap[K, V]) = l.asScala.toMap
    }

  /*
   * For transformations that may not require a copy, we don't biject on types
   * which would require a copy. To change inner type also, use connect[Seq[T], List[T], List[U]]
   */

  implicit def seq2List[A]: Bijection[Seq[A], List[A]] =
    new AbstractBijection[Seq[A], List[A]] {
      def apply(s: Seq[A]) = s.toList
      override def invert(l: List[A]) = l
    }
  implicit def seq2IndexedSeq[A]: Bijection[Seq[A], IndexedSeq[A]] =
    new AbstractBijection[Seq[A], IndexedSeq[A]] {
      def apply(s: Seq[A]) = s.toIndexedSeq
      override def invert(l: IndexedSeq[A]) = l
    }
  // This doesn't require a copy from Map -> Seq
  implicit def seq2Map[K, V]: Bijection[Seq[(K, V)], Map[K, V]] =
    new AbstractBijection[Seq[(K, V)], Map[K, V]] {
      def apply(s: Seq[(K, V)]) = s.toMap
      override def invert(l: Map[K, V]) = l.toSeq
    }
  // This doesn't require a copy from Set -> Seq
  implicit def seq2Set[T]: Bijection[Seq[T], Set[T]] =
    new AbstractBijection[Seq[T], Set[T]] {
      def apply(s: Seq[T]) = s.toSet
      override def invert(l: Set[T]) = l.toSeq
    }

  protected def trav2Vector[T, C >: Vector[T] <: Iterable[T]]: Bijection[C, Vector[T]] =
    new SubclassBijection[C, Vector[T]](classOf[Vector[T]]) {
      def applyfn(s: C) = {
        // Just build one:
        val bldr = new scala.collection.immutable.VectorBuilder[T]
        bldr ++= s
        bldr.result
      }
    }
  implicit def seq2Vector[T]: Bijection[Seq[T], Vector[T]] = trav2Vector[T, Seq[T]]
  implicit def indexedSeq2Vector[T]: Bijection[IndexedSeq[T], Vector[T]] =
    trav2Vector[T, IndexedSeq[T]]

  /**
    * Accepts a Bijection[A, B] and returns a bijection that can
    * transform iterable containers of A into iterable containers of B.
    *
    * Do not go from ordered to unordered containers;
    * Bijection[Iterable[A], Set[B]] is inaccurate, and really makes
    * no sense.
    */
  def toContainer[A, B, C <: IterableOnce[A], D <: IterableOnce[B]](
      implicit bij: ImplicitBijection[A, B],
      cd: Factory[B, D],
      dc: Factory[A, C]
  ): Bijection[C, D] =
    new AbstractBijection[C, D] {
      def apply(c: C) = {
        val builder = cd.newBuilder
        c.iterator.foreach { builder += bij(_) }
        builder.result()
      }
      override def invert(d: D) = {
        val builder = dc.newBuilder
        d.iterator.foreach { builder += bij.invert(_) }
        builder.result()
      }
    }

  implicit def betweenMaps[K1, V1, K2, V2](
      implicit kBijection: ImplicitBijection[K1, K2],
      vBijection: ImplicitBijection[V1, V2]
  ) =
    toContainer[(K1, V1), (K2, V2), Map[K1, V1], Map[K2, V2]]

  implicit def betweenVectors[T, U](implicit bij: ImplicitBijection[T, U]) =
    toContainer[T, U, Vector[T], Vector[U]]

  implicit def betweenIndexedSeqs[T, U](implicit bij: ImplicitBijection[T, U]) =
    toContainer[T, U, IndexedSeq[T], IndexedSeq[U]]

  implicit def betweenSets[T, U](implicit bij: ImplicitBijection[T, U]) =
    toContainer[T, U, Set[T], Set[U]]

  implicit def betweenSeqs[T, U](implicit bij: ImplicitBijection[T, U]) =
    toContainer[T, U, Seq[T], Seq[U]]

  implicit def betweenLists[T, U](implicit bij: ImplicitBijection[T, U]) =
    toContainer[T, U, List[T], List[U]]

  implicit def option[T, U](
      implicit bij: ImplicitBijection[T, U]
  ): Bijection[Option[T], Option[U]] =
    new AbstractBijection[Option[T], Option[U]] {
      override def apply(optt: Option[T]) = optt.map(bij.bijection)
      override def invert(optu: Option[U]) = optu.map(bij.bijection.inverse)
    }
  // Always requires a copy
  implicit def vector2List[A, B](
      implicit bij: ImplicitBijection[A, B]
  ): Bijection[Vector[A], List[B]] =
    toContainer[A, B, Vector[A], List[B]]

  implicit def indexedSeq2List[A, B](
      implicit bij: ImplicitBijection[A, B]
  ): Bijection[IndexedSeq[A], List[B]] =
    toContainer[A, B, IndexedSeq[A], List[B]]

  /**
    * This doesn't actually copy the Array, only wraps/unwraps with WrappedArray
    */
  implicit def array2Iterable[T: ClassTag]: Bijection[Array[T], Iterable[T]] =
    new AbstractBijection[Array[T], Iterable[T]] {
      override def apply(a: Array[T]) = a.toIterable
      override def invert(t: Iterable[T]) = t.toArray
    }

  /**
    * This doesn't actually copy the Array, only wraps/unwraps with WrappedArray
    */
  implicit def array2Seq[T: ClassTag]: Bijection[Array[T], Seq[T]] =
    new AbstractBijection[Array[T], Seq[T]] {
      override def apply(a: Array[T]) = a.toSeq
      override def invert(t: Seq[T]) = t.toArray
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy