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

rl.Uri.scala Maven / Gradle / Ivy

package rl

import java.lang.{ UnsupportedOperationException, Boolean }
import java.net.{ URI, URISyntaxException, IDN }

trait UriNode {
  def uriPart: String
  def normalize: UriNode

  def apply(): String
}

trait UriOperations {
  def +(other: Uri): Uri
  def normalize: Uri
  def /(other: Uri): Uri
}

trait Uri {
  def scheme: UriScheme
  def authority: Option[Authority]
  def segments: UriPath
  def query: QueryString
  def fragment: UriFragment

  lazy val user = authority flatMap (_.userInfo map (_.user))
  lazy val secret = authority flatMap (_.userInfo map (_.secret))
  lazy val host = authority map (_.host.value)
  lazy val port = authority map (_.port)
  lazy val path = segments()
  lazy val queryString = query()
  lazy val rawQuery = query.rawValue

  def originalUri: String
  def isAbsolute: Boolean
  def isRelative: Boolean
  def normalize: Uri = normalize(false)
  def normalize(stripCommonPrefixFromHost: Boolean = false): Uri

  def asciiString = {
    scheme.uriPart + authority.map(_.uriPart).getOrElse("") + segments.uriPart + query.uriPart + fragment.uriPart
  }
}

case class AbsoluteUri(scheme: Scheme, authority: Option[Authority], segments: UriPath, query: QueryString, fragment: UriFragment, originalUri: String = "") extends Uri {
  val isAbsolute: Boolean = true
  val isRelative: Boolean = false

  def normalize(stripCommonPrefixFromHost: Boolean = false) =
    copy(scheme.normalize, authority.map(_.normalize(stripCommonPrefixFromHost)), segments.normalize, query.normalize, fragment.normalize)
}

case class RelativeUri(authority: Option[Authority], segments: UriPath, query: QueryString, fragment: UriFragment, originalUri: String = "") extends Uri {
  val scheme = NoScheme

  val isAbsolute: Boolean = false
  val isRelative: Boolean = true

  def normalize(stripCommonPrefixFromHost: Boolean = false) =
    copy(authority.map(_.normalize(stripCommonPrefixFromHost)), segments.normalize, query.normalize, fragment.normalize)
}

case class FailedUri(throwable: Throwable, originalUri: String = "") extends Uri {

  private def noop = {
    val u = originalUri.blankOption getOrElse "not set"
    throw new UnsupportedOperationException("Parsing the uri '%s' failed." format u, throwable)
  }

  def fragment = noop

  def query = noop

  def segments = noop

  def authority = noop

  def scheme = noop

  val isRelative: Boolean = false

  val isAbsolute: Boolean = false

  def normalize(stripCommonPrefixFromHost: Boolean = false) = this
}

object Uri {

  /*
   * The regex to split a URI up into its parts for further processing
   * Source: http://tools.ietf.org/html/rfc3986#appendix-B
   */
  val UriParts = """^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?""".r

  def apply(uriString: String): Uri = {
    try {
      apply(URI.create(uriString))
    } catch {
      case e: URISyntaxException ⇒ {
        FailedUri(e, uriString)
      }
      case e: IllegalArgumentException ⇒ {
        FailedUri(e, uriString)
      }
    }
  }

  def apply(u: URI, originalUri: Option[String] = None): Uri = {
    try {
      val pth = parsePath(u.getRawPath.blankOption)

      if (u.isAbsolute) {
        AbsoluteUri(
          Scheme(u.getScheme),
          u.getRawAuthority.blankOption.map(a ⇒ Authority(IDN.toASCII(a))),
          pth,
          QueryString(u.getRawQuery),
          UriFragment(u.getRawFragment),
          originalUri getOrElse u.toString)
      } else {
        RelativeUri(
          u.getRawAuthority.blankOption.map(a ⇒ Authority(IDN.toASCII(a))),
          pth,
          QueryString(u.getRawQuery),
          UriFragment(u.getRawFragment),
          originalUri getOrElse u.toString)
      }
    } catch {
      case e: NullPointerException ⇒ {
        FailedUri(e, originalUri getOrElse u.toString)
      }
      case e: Throwable ⇒ {
        FailedUri(e, originalUri getOrElse u.toString)
      }
    }
  }

  private def parsePath(text: Option[String]): UriPath = {
    text match {
      case None                           ⇒ EmptyPath
      case Some(pt) if pt.trim == "/"     ⇒ EmptyPath
      case Some(pt) if pt.startsWith("/") ⇒ AbsolutePath(pt.split("/").drop(1).toList)
      case Some(pt)                       ⇒ RelativePath(pt.split("/"))
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy