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

scalapb.FieldMaskTree.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-alpha.1
Show newest version
package scalapb

import com.google.protobuf.field_mask.FieldMask
import scalapb.FieldMaskTree.Empty
import scalapb.descriptors.{Descriptor, PMessage, ScalaType}

import scala.collection.SortedMap

private[scalapb] case class FieldMaskTree(nodes: SortedMap[String, FieldMaskTree]) {

  def applyToMessage[M <: GeneratedMessage: GeneratedMessageCompanion](message: M): M = {
    val companion        = implicitly[GeneratedMessageCompanion[M]]
    val filteredPMessage = applyToMessage(message.toPMessage)
    companion.messageReads.read(filteredPMessage)
  }

  def containsField[M <: GeneratedMessage: GeneratedMessageCompanion](fieldNumber: Int): Boolean = {
    val descriptor = implicitly[GeneratedMessageCompanion[M]].scalaDescriptor
    descriptor.findFieldByNumber(fieldNumber) match {
      case Some(field) => nodes.contains(field.name)
      case None        => false
    }
  }

  def fieldMask: FieldMask = FieldMask(paths)

  def isValidFor[M <: GeneratedMessage: GeneratedMessageCompanion]: Boolean = {
    val descriptor = implicitly[GeneratedMessageCompanion[M]].scalaDescriptor
    isValidFor(descriptor)
  }

  private def applyToMessage(message: PMessage): PMessage = {
    val fieldValues = message.value.keys.flatMap { fieldDescriptor =>
      val value = (message.value.apply(fieldDescriptor), nodes.get(fieldDescriptor.name)) match {
        case (pValue, Some(Empty))                 => Some(pValue)
        case (subMessage: PMessage, Some(subTree)) => Some(subTree.applyToMessage(subMessage))
        case _                                     => None
      }
      value.map(fieldDescriptor -> _)
    }.toMap
    PMessage(fieldValues)
  }

  private def isValidFor(descriptor: Descriptor): Boolean = {
    nodes.forall { case (field, tree) =>
      descriptor.findFieldByName(field) match {
        case None => false
        case Some(field) =>
          if (tree == FieldMaskTree.Empty) {
            true
          } else {
            field.scalaType match {
              case ScalaType.Message(m) if !field.isRepeated => tree.isValidFor(m)
              case _                                         => false
            }
          }
      }
    }
  }

  private def paths: Vector[String] = {
    nodes.toVector.flatMap {
      case (field, FieldMaskTree.Empty) => Vector(field)
      case (field, subTree) =>
        subTree.paths.map(path => s"$field${FieldMaskTree.FieldSeparator}$path")
    }
  }
}

private[scalapb] object FieldMaskTree {

  val Empty: FieldMaskTree = FieldMaskTree(SortedMap.empty[String, FieldMaskTree])

  private val FieldSeparator = '.'

  def apply(paths: Seq[String]): FieldMaskTree = {
    paths.map(fromPath).foldLeft[FieldMaskTree](Empty)(union)
  }

  def apply(fieldMask: FieldMask): FieldMaskTree = {
    apply(fieldMask.paths)
  }

  def union(tree1: FieldMaskTree, tree2: FieldMaskTree): FieldMaskTree = (tree1, tree2) match {
    case (Empty, tree) => tree
    case (tree, Empty) => tree
    case (FieldMaskTree(nodes1), FieldMaskTree(nodes2)) =>
      val fields: Seq[String] = (nodes1.keySet ++ nodes2.keySet).toSeq
      val tree = SortedMap(fields.map { field =>
        val subTree = (nodes1.get(field), nodes2.get(field)) match {
          case (Some(Empty), _) => Empty
          case (_, Some(Empty)) => Empty
          case (mask1, mask2)   => union(mask1.getOrElse(Empty), mask2.getOrElse(Empty))
        }
        field -> subTree
      }: _*)
      FieldMaskTree(tree)
  }

  private def fromPath(path: String): FieldMaskTree = {
    path
      .split(FieldSeparator)
      .filter(_.nonEmpty)
      .foldRight[FieldMaskTree](Empty) { case (field, tree) =>
        FieldMaskTree(SortedMap(field -> tree))
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy