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

com.swoval.files.AppleDirectoryWatcher.scala Maven / Gradle / Ivy

The newest version!
package com.swoval.files

import java.util.concurrent.atomic.AtomicBoolean

import com.swoval.files.DirectoryWatcher.Callback
import com.swoval.files.FileWatchEvent.{ Create, Delete, Modify }
import com.swoval.files.apple.{ FileEvent, FileEventsApi, Flags }
import com.swoval.files.compat._

import scala.annotation.tailrec
import scala.collection.mutable
import scala.concurrent.duration.Duration

class AppleDirectoryWatcher(latency: Duration, flags: Flags.Create, executor: Executor)(
    override val onFileEvent: Callback,
    val onStreamRemoved: String => Unit = _ => {})
    extends DirectoryWatcher {
  override def close(): Unit = if (closed.compareAndSet(false, true)) {
    lock.synchronized { streams.clear() }
    fileSystemApi.close()
    executor.close()
  }

  def register(path: Path, recursive: Boolean): Boolean = register(path, flags.value)
  def register(path: Path, flags: Int): Boolean = {
    if (path.isDirectory && !alreadyWatching(path)) {
      fileSystemApi.createStream(path.fullName, latency.toNanos / 1e9, flags) match {
        case -1 => System.err.println(s"Error watching $path.")
        case id => lock.synchronized(streams += path.fullName -> id)
      }
    }
    true
  }

  def unregister(path: Path): Unit = unregister(path.name)
  private def unregister(path: String): Unit = {
    lock.synchronized(streams get path match {
      case Some(streamHandle) if streamHandle != 0 =>
        streams -= path
        Some(streamHandle)
      case _ => None // Nothing registered, ignore event
    }) foreach fileSystemApi.stopStream
  }

  private[this] val streams = mutable.Map.empty[String, Int]
  private[this] val lock = new Object
  private[this] val closed = new AtomicBoolean(false)
  private[this] val onFileEventImpl = new FileEventsApi.Consumer[FileEvent] {
    override def accept(fe: FileEvent): Unit = {
      executor.run(onFileEvent({
        val path = Path(fe.fileName)
        if (fe.itemIsFile) {
          fe match {
            case e if e.isNewFile && path.exists =>
              FileWatchEvent(path, Create)
            case e if e.isRemoved || !path.exists =>
              FileWatchEvent(path, Delete)
            case _ =>
              FileWatchEvent(path, Modify)
          }
        } else if (path.exists) {
          FileWatchEvent(path, Modify)
        } else {
          FileWatchEvent(path, Delete)
        }
      }))
    }
  }
  private[this] val onStreamEvent = new FileEventsApi.Consumer[String] {
    override def accept(s: String): Unit = {
      if (!closed.get) executor.run {
        lock.synchronized(streams -= s)
        onStreamRemoved(s)
      }
    }
  }
  private[this] val fileSystemApi: FileEventsApi = try {
    FileEventsApi.apply(onFileEventImpl, onStreamEvent)
  } catch { case _: Throwable => sys.exit(1) }

  @tailrec @inline
  private[this] final def alreadyWatching(path: Path): Boolean = {
    if (path == path.getRoot) false
    else (streams contains path.fullName) || alreadyWatching(path.getParent)
  }
}

object AppleDirectoryWatcher {
  def apply(latency: Duration, flags: Flags.Create)(
      onFileEvent: Callback,
      onStreamRemoved: String => Unit = _ => {}): AppleDirectoryWatcher = {
    val e: Executor = platform.makeExecutor("com.swoval.files.AppleDirectoryWatcher.executorThread")
    new AppleDirectoryWatcher(latency, flags, e)(onFileEvent, onStreamRemoved)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy