org.apache.pekko.util.WildcardIndex.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pekko-actor_2.13 Show documentation
Show all versions of pekko-actor_2.13 Show documentation
Apache Pekko is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* license agreements; and to You under the Apache License, version 2.0:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* This file is part of the Apache Pekko project, which was derived from Akka.
*/
/*
* Copyright (C) 2009-2022 Lightbend Inc.
*/
package org.apache.pekko.util
import scala.annotation.tailrec
import scala.collection.immutable.HashMap
private[pekko] final case class WildcardIndex[T](
wildcardTree: WildcardTree[T] = WildcardTree[T](),
doubleWildcardTree: WildcardTree[T] = WildcardTree[T]()) {
def insert(elems: Array[String], d: T): WildcardIndex[T] = elems.lastOption match {
case Some("**") => copy(doubleWildcardTree = doubleWildcardTree.insert(elems.iterator, d))
case Some(_) => copy(wildcardTree = wildcardTree.insert(elems.iterator, d))
case _ => this
}
def find(elems: Iterable[String]): Option[T] =
(if (wildcardTree.isEmpty) {
if (doubleWildcardTree.isEmpty) {
WildcardTree[T]() // empty
} else {
doubleWildcardTree.findWithTerminalDoubleWildcard(elems.iterator)
}
} else {
val withSingleWildcard = wildcardTree.findWithSingleWildcard(elems.iterator)
if (withSingleWildcard.isEmpty) {
doubleWildcardTree.findWithTerminalDoubleWildcard(elems.iterator)
} else {
withSingleWildcard
}
}).data
def isEmpty: Boolean = wildcardTree.isEmpty && doubleWildcardTree.isEmpty
}
private[pekko] object WildcardTree {
private val empty = new WildcardTree[Nothing]()
def apply[T](): WildcardTree[T] = empty.asInstanceOf[WildcardTree[T]]
}
private[pekko] final case class WildcardTree[T](
data: Option[T] = None,
children: Map[String, WildcardTree[T]] = HashMap[String, WildcardTree[T]](),
wildcardSuffixChildren: Map[String, WildcardTree[T]] = HashMap[String, WildcardTree[T]]()) {
def isEmpty: Boolean = data.isEmpty && children.isEmpty
def insert(elems: Iterator[String], d: T): WildcardTree[T] =
if (!elems.hasNext) {
copy(data = Some(d))
} else {
val e = elems.next()
if (e != "**" && e.endsWith("**"))
throw new IllegalArgumentException(
"double wildcard can't be used as a suffix (e.g. /user/actor**), only as a full subPath element (e.g. /user/actor/**)")
else if (e != "*" && e != "**" && e.endsWith("*"))
copy(
wildcardSuffixChildren = wildcardSuffixChildren
.updated(e.stripSuffix("*"), wildcardSuffixChildren.getOrElse(e, WildcardTree[T]()).insert(elems, d)))
else
copy(children = children.updated(e, children.getOrElse(e, WildcardTree[T]()).insert(elems, d)))
}
@tailrec def findWithSingleWildcard(elems: Iterator[String]): WildcardTree[T] =
if (!elems.hasNext) this
else {
val nextElement = elems.next()
children.get(nextElement) match {
case Some(branch) => branch.findWithSingleWildcard(elems)
case None =>
children.get("*") match {
case Some(branch) => branch.findWithSingleWildcard(elems)
case None =>
val maybeWildcardSuffixMatchingBranch = wildcardSuffixChildren.collectFirst {
case (key, branch) if nextElement.startsWith(key) => branch
}
maybeWildcardSuffixMatchingBranch match {
case Some(branch) => branch.findWithSingleWildcard(elems)
case None => WildcardTree[T]()
}
}
}
}
@tailrec def findWithTerminalDoubleWildcard(
elems: Iterator[String],
alt: WildcardTree[T] = WildcardTree[T]()): WildcardTree[T] = {
if (!elems.hasNext) this
else {
val newAlt = children.getOrElse("**", alt)
children.get(elems.next()) match {
case Some(branch) => branch.findWithTerminalDoubleWildcard(elems, newAlt)
case None =>
children.get("*") match {
case Some(branch) => branch.findWithTerminalDoubleWildcard(elems, newAlt)
case None => newAlt
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy