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

sbt.internal.nio.Observers.scala Maven / Gradle / Ivy

/*
 * sbt IO
 * Copyright Scala Center, Lightbend, and Mark Harrah
 *
 * Licensed under Apache License 2.0
 * SPDX-License-Identifier: Apache-2.0
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package sbt.internal.nio

import java.io.IOException
import java.nio.file.{ Path => NioPath }
import java.util.WeakHashMap
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger

import sbt.nio.file.Glob
import sbt.nio.file.Glob.GlobOps

import scala.util.control.NonFatal

@FunctionalInterface
private[sbt] trait Observer[-T] extends AutoCloseable {

  /**
   * Run a callback
   * @param t the generic type of events
   */
  def onNext(t: T): Unit

  /**
   * This is for managed observers that may need to be removed once the caller is done with them.
   * See [[Observers]].
   */
  override def close(): Unit = {}
}

/**
 * An object that monitors a file system. The interface is very similar to that provided by other
 * libraries/frameworks, such as [[http://reactivex.io/intro.html rxJava]]. When it detects changes
 * in the file system, it will invoke a set of user specified callbacks. The Observable also
 * allows the user to add and removes paths to monitor.
 *
 * @tparam T the generic type of observable values
 */
private[sbt] trait Observable[+T] extends AutoCloseable {

  /**
   * Add callbacks to be invoked on file events.
   *
   * @param observer the callbacks to invoke
   * @return a handle to the callback.
   */
  def addObserver(observer: Observer[T]): AutoCloseable
}

private[sbt] trait ObservablePaths[+T] extends Observable[(NioPath, T)]

private[sbt] object Observable {
  def map[T, R](observable: Observable[T], f: T => R): Observable[R] = new Observable[R] {
    override def addObserver(observer: Observer[R]): AutoCloseable =
      observable.addObserver(t => observer.onNext(f(t)))
    override def close(): Unit = observable.close()
  }
  def filter[T](observable: Observable[T], f: T => Boolean): Observable[T] = new Observable[T] {
    override def addObserver(observer: Observer[T]): AutoCloseable =
      observable.addObserver(t => if (f(t)) observer.onNext(t))
    override def close(): Unit = observable.close()
  }
}

/**
 * Aggregates a collection of [[Observer]]s into a single [[Observer]]. The callbacks for the
 * generated [[Observer]] invoke the corresponding callback for each of the [[Observer]]s that
 * are added via [[addObserver]].
 *
 * @tparam T the generic type of value instances for the [[FileTreeRepository]]
 */
private[sbt] class Observers[T] extends Observer[T] with Observable[T] {
  private class Handle(id: Int) extends AutoCloseable {
    override def close(): Unit = {
      observers.remove(id)
      ()
    }
  }
  private[this] val id = new AtomicInteger(0)
  private[this] val observers = new ConcurrentHashMap[Int, Observer[T]]
  private[this] val observables = new WeakHashMap[AutoCloseable, Unit]

  private[sbt] def addObservable(observable: Observable[T]): AutoCloseable =
    observables.synchronized {
      val handle = observable.addObserver(this)
      observables.put(handle, ())
      handle
    }
  override def addObserver(observer: Observer[T]): AutoCloseable = {
    val observerId = id.incrementAndGet()
    observers.put(observerId, observer)
    new Handle(observerId)
  }

  override def close(): Unit = {
    observers.clear()
    observables.synchronized {
      observables.keySet.forEach(_.close())
      observables.clear()
    }
  }
  override def toString: String =
    s"Observers(observers = ${observers.values}, observables = ${observables.keySet})"
  override def onNext(t: T): Unit = observers.values.forEach { o =>
    try o.onNext(t)
    catch { case NonFatal(_) => }
  }
}
private[sbt] class RegisterableObservable[T](val delegate: Observers[FileEvent[T]]) extends AnyVal {
  def register(glob: Glob): Either[IOException, Observable[FileEvent[T]]] =
    Registerable(glob, delegate)
}

/**
 * A dynamically configured monitor of the file system. New paths can be added and removed from
 * monitoring with register / unregister.
 */
private[sbt] trait Registerable[+T] extends AutoCloseable {

  /**
   * Register a glob for monitoring.
   *
   * @param glob Glob
   * @return an Either that is a Right when register has no errors and a Left if an IOException is
   *         thrown while registering the path. The result should be true if the path has
   *         never been previously registered or if the recursive flag flips from false to true.
   */
  def register(glob: Glob): Either[IOException, Observable[T]]
}
private[sbt] object Registerable {
  def apply[T](
      glob: Glob,
      delegate: Observers[FileEvent[T]]
  ): Either[IOException, Observable[FileEvent[T]]] = {
    val filter = glob.toFileFilter
    val underlying = new Observers[FileEvent[T]]
    val observers =
      Observable.filter(underlying, (e: FileEvent[T]) => filter.accept(e.path.toFile))
    val handle = delegate.addObserver(underlying)

    Right(new Observable[FileEvent[T]] {
      override def addObserver(observer: Observer[FileEvent[T]]): AutoCloseable =
        observers.addObserver(observer)
      override def close(): Unit = handle.close()
    })
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy