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

org.scalatra.util.Inflector.scala Maven / Gradle / Ivy

package org.scalatra.util

import java.util.Locale.ENGLISH
import annotation.tailrec
import scala.util.matching.Regex

trait Inflector {

  def titleize(word: String): String =
    """\b([a-z])""".r.replaceAllIn(humanize(underscore(word)), _.group(0).toUpperCase(ENGLISH))
  def humanize(word: String): String = capitalize(word.replace("_", " "))
  def camelize(word: String): String = {
    val w = pascalize(word)
    w.substring(0, 1).toLowerCase(ENGLISH) + w.substring(1)
  }
  def pascalize(word: String): String = {
    val lst = word.split("_").toList
    (lst.headOption.map(s => s.substring(0, 1).toUpperCase(ENGLISH) + s.substring(1)).get ::
      lst.tail.map(s => s.substring(0, 1).toUpperCase + s.substring(1))).mkString("")
  }
  def underscore(word: String): String = {
    val spacesPattern = "[-\\s]".r
    val firstPattern = "([A-Z]+)([A-Z][a-z])".r
    val secondPattern = "([a-z\\d])([A-Z])".r
    val replacementPattern = "$1_$2"
    spacesPattern.replaceAllIn(
      secondPattern.replaceAllIn(
        firstPattern.replaceAllIn(
          word, replacementPattern), replacementPattern), "_").toLowerCase
  }

  def capitalize(word: String): String =
    word.substring(0, 1).toUpperCase(ENGLISH) + word.substring(1).toLowerCase(ENGLISH)
  def uncapitalize(word: String): String =
    word.substring(0, 1).toLowerCase(ENGLISH) + word.substring(1)
  def ordinalize(word: String): String = ordanize(word.toInt, word)
  def ordinalize(number: Int): String = ordanize(number, number.toString)
  private def ordanize(number: Int, numberString: String) = {
    val nMod100 = number % 100
    if (nMod100 >= 11 && nMod100 <= 13) numberString + "th"
    else {
      (number % 10) match {
        case 1 => numberString + "st"
        case 2 => numberString + "nd"
        case 3 => numberString + "rd"
        case _ => numberString + "th"
      }
    }
  }
  def dasherize(word: String): String = underscore(word).replace('_', '-')
  def pluralize(word: String): String = applyRules(plurals, word)
  def singularize(word: String): String = applyRules(singulars, word)

  private object Rule {
    def apply(kv: (String, String)) = new Rule(kv._1, kv._2)
  }
  private class Rule(pattern: String, replacement: String) {

    private val regex = ("""(?i)%s""" format pattern).r

    def apply(word: String) = {
      if (regex.findFirstIn(word).isEmpty) {
        None
      } else {
        val m = regex.replaceAllIn(word, replacement)
        if (m == null || m.trim.isEmpty) None
        else Some(m)
      }
    }
  }
  private implicit def tuple2Rule(pair: (String, String)): Rule = Rule(pair)

  @tailrec
  private def applyRules(collection: List[Rule], word: String): String = {
    if (uncountables.contains(word.toLowerCase(ENGLISH))) word
    else {
      if (collection.isEmpty) return word
      val m = collection.head(word)
      if (m.isDefined) m.get // using getOrElse doesn't allow for @tailrec optimization
      else applyRules(collection.tail, word)
    }
  }

  private var plurals = List[Rule]()

  private var singulars = List[Rule]()
  private var uncountables = List[String]()

  def addPlural(pattern: String, replacement: String): Unit = { plurals ::= pattern -> replacement }
  def addSingular(pattern: String, replacement: String): Unit = { singulars ::= pattern -> replacement }
  def addIrregular(singular: String, plural: String): Unit = {
    plurals ::= (("(" + singular(0) + ")" + singular.substring(1) + "$") -> ("$1" + plural.substring(1)))
    singulars ::= (("(" + plural(0) + ")" + plural.substring(1) + "$") -> ("$1" + singular.substring(1)))
  }
  def addUncountable(word: String) = uncountables ::= word

  def interpolate(text: String, vars: Map[String, String]) =
    """\#\{([^}]+)\}""".r.replaceAllIn(text, (_: Regex.Match) match {
      case Regex.Groups(v) => vars.getOrElse(v, "")
    })

}

trait InflectorImports {

  implicit def string2InflectorString(word: String): Inflector.InflectorString = new Inflector.InflectorString(word)
  implicit def int2InflectorInt(number: Int): Inflector.InflectorInt = new Inflector.InflectorInt(number)

}

object InflectorImports extends InflectorImports

object Inflector extends Inflector {

  class InflectorString(private val word: String) extends AnyVal {
    def titleize = Inflector.titleize(word)
    def humanize = Inflector.humanize(word)
    def camelize = Inflector.camelize(word)
    def pascalize = Inflector.pascalize(word)
    def underscore = Inflector.underscore(word)
    def dasherize = Inflector.dasherize(word)
    def uncapitalize = Inflector.uncapitalize(word)
    def ordinalize = Inflector.ordinalize(word)
    def pluralize = Inflector.pluralize(word)
    def singularize = Inflector.singularize(word)
    def fill(values: (String, String)*) = Inflector.interpolate(word, Map(values: _*))
  }

  class InflectorInt(private val number: Int) extends AnyVal {
    def ordinalize = Inflector.ordinalize(number)
  }

  addPlural("$", "s")
  addPlural("s$", "s")
  addPlural("(ax|test)is$", "$1es")
  addPlural("(octop|vir|alumn|fung)us$", "$1i")
  addPlural("(alias|status)$", "$1es")
  addPlural("(bu)s$", "$1ses")
  addPlural("(buffal|tomat|volcan)o$", "$1oes")
  addPlural("([ti])um$", "$1a")
  addPlural("sis$", "ses")
  addPlural("(?:([^f])fe|([lr])f)$", "$1$2ves")
  addPlural("(hive)$", "$1s")
  addPlural("([^aeiouy]|qu)y$", "$1ies")
  addPlural("(x|ch|ss|sh)$", "$1es")
  addPlural("(matr|vert|ind)ix|ex$", "$1ices")
  addPlural("([m|l])ouse$", "$1ice")
  addPlural("^(ox)$", "$1en")
  addPlural("(quiz)$", "$1zes")

  addSingular("s$", "")
  addSingular("(n)ews$", "$1ews")
  addSingular("([ti])a$", "$1um")
  addSingular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis")
  addSingular("(^analy)ses$", "$1sis")
  addSingular("([^f])ves$", "$1fe")
  addSingular("(hive)s$", "$1")
  addSingular("(tive)s$", "$1")
  addSingular("([lr])ves$", "$1f")
  addSingular("([^aeiouy]|qu)ies$", "$1y")
  addSingular("(s)eries$", "$1eries")
  addSingular("(m)ovies$", "$1ovie")
  addSingular("(x|ch|ss|sh)es$", "$1")
  addSingular("([m|l])ice$", "$1ouse")
  addSingular("(bus)es$", "$1")
  addSingular("(o)es$", "$1")
  addSingular("(shoe)s$", "$1")
  addSingular("(cris|ax|test)es$", "$1is")
  addSingular("(octop|vir|alumn|fung)i$", "$1us")
  addSingular("(alias|status)es$", "$1")
  addSingular("^(ox)en", "$1")
  addSingular("(vert|ind)ices$", "$1ex")
  addSingular("(matr)ices$", "$1ix")
  addSingular("(quiz)zes$", "$1")

  addIrregular("person", "people")
  addIrregular("man", "men")
  addIrregular("child", "children")
  addIrregular("sex", "sexes")
  addIrregular("move", "moves")
  addIrregular("goose", "geese")
  addIrregular("alumna", "alumnae")

  addUncountable("equipment")
  addUncountable("information")
  addUncountable("rice")
  addUncountable("money")
  addUncountable("species")
  addUncountable("series")
  addUncountable("fish")
  addUncountable("sheep")
  addUncountable("deer")
  addUncountable("aircraft")
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy