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

org.hammerlab.paths.Path.scala Maven / Gradle / Ivy

The newest version!
package org.hammerlab.paths

import java.io.{ InputStream, ObjectStreamException, OutputStream, PrintStream, PrintWriter }
import java.net.{ URI, URISyntaxException }
import java.nio.file.Files.{ createDirectories, newDirectoryStream, newInputStream, newOutputStream, readAllBytes }
import java.nio.file.spi.FileSystemProvider
import java.nio.file.{ DirectoryStream, FileSystemNotFoundException, Files, Paths, StandardOpenOption, Path ⇒ JPath }
import StandardOpenOption.{ APPEND, CREATE }

import caseapp.core.argparser._
import com.sun.nio.zipfs.ZipPath
import org.apache.commons.io.FilenameUtils.getExtension

import scala.collection.JavaConverters._

/**
 * Wrapper around [[java.nio.file.Path]] that adds useful methods, is [[Serializable]], and fixes some
 * provider/custom-scheme-loading issues (see [[FileSystems]]).
 */
case class Path(path: JPath) {

  /**
   * Delegate serialization to [[SerializablePath]]
   */
  @throws[ObjectStreamException]
  def writeReplace: Object = {
    val sp = new SerializablePath
    sp.str = toString
    sp
  }

  /**
   * In general, delegate to [[URI]]'s `toString`.
   *
   * In case of relative paths, take a short-cut to preserve relative-ness.
   */
  override def toString: String =
    if (path.isAbsolute)
      uri.toString
    else
      path match {
        case _: ZipPath ⇒
          uri.toString
        case _ ⇒
          path.toString
      }

  def uri: URI = path.toUri

  def extension: String = getExtension(basename)

  def exists: Boolean = Files.exists(path)

  def parent: Path = parentOpt.getOrElse(this)
  def parentOpt: Option[Path] = Option(path.getParent).map(Path(_))

  def depth: Int =
    if (path.isAbsolute)
      parentOpt
        .map(_.depth + 1)
        .getOrElse(0)
    else
      Path(path.toAbsolutePath).depth

  def basename: String = path.getFileName.toString
  def basenameNoExtension: String = {
    val xtn = extension
    if (xtn.isEmpty)
      basename
    else
      basename.dropRight(xtn.length + 1)
  }
  def basename(extension: Boolean = true): String =
    if (extension)
      basename
    else
      basenameNoExtension

  def size: Long = Files.size(path)

  def endsWith(suffix: String): Boolean = basename.endsWith(suffix)

  def isFile: Boolean = Files.isRegularFile(path)
  def isDirectory: Boolean = Files.isDirectory(path)

  def walk: Iterator[Path] =
    if (!exists)
      Iterator()
    else
      Iterator(this) ++
        Files.list(path)
          .iterator()
          .asScala
          .map(Path(_))
          .flatMap(
            p ⇒
              if (p.isDirectory)
                p.walk
              else
                Iterator(p)
          )

  def delete(recursive: Boolean = false): Unit = {
    if (recursive) {
      Files.list(path)
        .iterator()
        .asScala
        .map(Path(_))
        .foreach(
          p ⇒
            if (p.isDirectory)
              p.delete(recursive = true)
            else
              Files.delete(p)
        )
    }
    Files.delete(path)
  }

  def mkdirs: Unit =
    parentOpt.foreach(
      parent ⇒
        if (!parent.exists)
          createDirectories(parent)
    )

  def  inputStream:  InputStream = newInputStream(path)
  def outputStream: OutputStream = newOutputStream(path)
  def  printStream:  PrintStream = new PrintStream(newOutputStream(path))

  def outputStream(append: Boolean, mkdirs: Boolean = false): OutputStream = {
    if (mkdirs)
      this.mkdirs

    if (append)
      newOutputStream(path, CREATE, APPEND)
    else
      newOutputStream(path)
  }
  def outputStream(mkdirs: Boolean): OutputStream = {
    if (mkdirs)
      this.mkdirs

    newOutputStream(path)
  }

  def printStream(append: Boolean, mkdirs: Boolean = false): PrintStream =
    new PrintStream(
      outputStream(
        append,
        mkdirs
      )
    )
  def printStream(mkdirs: Boolean): PrintStream =
    new PrintStream(
      outputStream(
        mkdirs
      )
    )

  private implicit def toScala(dirStream: DirectoryStream[JPath]): Iterator[Path] =
    dirStream
      .iterator()
      .asScala
      .map(Path(_))

  def list(glob: String): Iterator[Path] = newDirectoryStream(path, glob)
  def list: Iterator[Path] = newDirectoryStream(path)

  /**
   * Append `suffix` to the basename of this [[Path]].
   */
  def +(suffix: String): Path = Path(path.resolveSibling(basename + suffix))

  def /(basename: String): Path = Path(path.resolve(basename))
  def /(basename: Symbol): Path = Path(path.resolve(basename.name))

  def lines: Iterator[String] =
    Files
      .lines(path)
      .iterator
      .asScala

  def read: String = new String(readBytes)

  def readBytes: Array[Byte] = readAllBytes(path)

  def write(str: String): Unit = {
    val os = outputStream
    os.write(str.getBytes())
    os.close()
  }

  def writeLines(lines: Iterable[String]): Unit = {
    val pw = new PrintWriter(outputStream)
    lines.foreach(pw.println)
    pw.close()
  }
}

object Path {

  import FileSystems.init

  private def get(pathStr: String): JPath = {
    init()
    Paths.get(pathStr)
  }

  private def get(uri: URI): JPath = {
    init()
    Paths.get(uri)
  }

  def providers: Seq[FileSystemProvider] = {
    init()
    FileSystemProvider
      .installedProviders()
      .asScala
  }

  /**
   * If a [[Path]] is instantiated from a "jar"-scheme [[URI]] before [[FileSystems.init()]] has been called, a
   * [[FileSystemNotFoundException]] can occur.
   *
   * Get out in front of that here, forcing a [[FileSystems.init()]] if necessary.
   */
  def registerJarFilesystem(uri: URI): Unit = {
    providers
      .find(_.getScheme == "jar")
      .foreach(
        p ⇒
          try {
            p.getFileSystem(uri)
          } catch {
            case _: FileSystemNotFoundException ⇒
              p.newFileSystem(
                uri,
                Map.empty[String, Any].asJava
              )
          }
      )
  }

  def apply(pathStr: String): Path =
    try {
      val uri = new URI(pathStr)
      uri.getScheme match {
        case null ⇒
          new Path(get(pathStr))
        case "jar" ⇒
          registerJarFilesystem(uri)
          Path(uri)
        case _ ⇒
          Path(uri)
      }
    } catch {
      case _: URISyntaxException ⇒
        new Path(get(pathStr))
    }

  def apply(uri: URI): Path =
    uri.getScheme match {
      case "jar" ⇒
        registerJarFilesystem(uri)
        Path(get(uri))
      case _ ⇒
        Path(get(uri))
    }

  implicit def toJava(path: Path): JPath = path.path

  implicit val parser: ArgParser[Path] =
    SimpleArgParser.from("path") {
      str ⇒
        Right(
          Path(str)
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy