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

molecule.io.Input.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2013 Alcatel-Lucent.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 * Licensed to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package molecule
package io

import stream.IChan
import stream.ichan.NilIChan

/**
 * A process-level input channel.
 *
 *  @tparam  A    the type of the input's messages
 *
 */
abstract class Input[+A] extends SInput[A] with RInput[A] {

  /**
   * Read a single value using a parser or raise the signal if the input is closed
   *
   *  @param parser the parser used to parse a single element.
   *  @tparam B the type of the pareser element
   *
   *  @return  the next element of this input.
   */
  def read[C >: A, B](parser: parsing.Parser[C, B]): IO[B]

  /**
   * Builds a new input by applying a function to all messages of this input.
   *
   *  @param f      the function to apply to each message.
   *  @tparam B     the message type of the returned input.
   *  @return       a new input resulting from applying the given function
   *                `f` to each message of this input.
   *
   *  @usecase def map[B](f: A => B): Input[B]
   */
  def map[B: Message](f: A => B): Input[B]

  /**
   * Builds a new debugging input that prints every message received.
   *
   *  @param label  the label to put in front of each debug line.
   *  @param f      A function converting messages to a string.
   *  @return       The same input excepted each message will be printed.
   *
   *  @usecase def debug[B](label:String, f: A => B): Input[B]
   */
  def debug(label: String, f: A => String = _.toString): Input[A]

  /**
   * Builds a new input by applying a partial function to all messages of this input
   *  on which the function is defined.
   *
   *  @param pf     the partial function which filters and maps the input.
   *  @tparam B     the message type of the returned collection.
   *  @return       a new input resulting from applying the partial function
   *                `pf` to each message on which it is defined.
   *                The order of the messages is preserved.
   *
   *  @usecase def collect[B](pf: PartialFunction[A, B]): Input[B]
   */
  def collect[B: Message](pf: PartialFunction[A, B]): Input[B]

  /**
   * Builds a new input by applying a function to all messages of this input
   *  and concatenating the results.
   *
   *  @param f      the function to apply to each message.
   *  @tparam B     the message type of the returned input.
   *  @return       a new input resulting from applying the given collection-valued function
   *                `f` to each message of this input and concatenating the results.
   *
   *  @usecase def flatMap[B](f: A => Seg[B]): Input[B]
   *
   *  @return       a new input resulting from applying the given collection-valued function
   *                `f` to each message of this input and concatenating the results.
   */
  def flatMap[B: Message](f: A => Seg[B]): Input[B]

  /**
   * Produces an input containing cummulative results of applying the operator going first to last message.
   *
   * @tparam B      the type of the messages in the resulting input
   * @param z       the initial state
   * @param op      the binary operator applied to the intermediate state and the message
   * @return        input with intermediate results
   */
  def smap[S, B: Message](z: S)(fsm: (S, A) => (S, B)): Input[B]

  /**
   * Produces an input resulting from applying a repeatedly a parser combinator to this input stream.
   *
   * @tparam B      the type of the messages in the resulting input
   * @tparam C      the type of the messages parsed
   * @param parser  a parser combinator
   * @return        input with parsed results
   */
  def parse[C >: A: Message, B: Message](parser: parsing.Parser[C, B]): Input[B]

  /**
   * Produces a collection containing cummulative results of applying the operator going
   * first to last message.
   *
   * @tparam B      the type of the messages in the resulting input
   * @param z       the initial value
   * @param op      the binary operator applied to the intermediate result and the message
   * @return        input with intermediate results
   */
  def scan[B: Message](z: B)(op: (B, A) => B): Input[B]

  /**
   * Builds a new input that compresses the content of each segment into a single value.
   *
   *  @param f      A function converting segments to a single value.
   *  @return       The compressed input.
   */
  def compress[B: Message](f: seg.Seg[A] => B): Input[B]

  /**
   * Selects all messages of this input which satisfy a predicate.
   *
   *  @param p     the predicate used to test messages.
   *  @return      a new input consisting of all messages of this input that satisfy the given
   *               predicate `p`. The order of the messages is preserved.
   */
  def filter(p: A => Boolean): Input[A]

  /**
   * Converts this input stream of traversable collections into
   *  an input stream in which all message collections are concatenated.
   *  @tparam B the type of the messages of each traversable collection.
   *  @param asTraversable an implicit conversion which asserts that the message type of this
   *         input is a `Traversable`.
   *  @param message an implicit message definition for the message type of the
   *         `Traversable`.
   *  @return a new input resulting from concatenating all the `Traversable` collections.
   *  @usecase def flatten[B]: Input[B]
   */
  def flatten[B](implicit message: Message[B], asTraversable: A => /*<:: A: Message](x: B): Input[B]

  /**
   * Prepend the messages of a given segment in front of this input.
   *  @param prefix  The segment to prepend.
   *  @return  an input which produces that segment first and
   *           then continues with the remaining of the stream.
   *  @usecase def ++:(prefix: Seg[A]): Input[A]
   */
  def ++:[B >: A: Message](prefix: Seg[B]): Input[B]

  /**
   * Skip the first ''n'' messages of this input, or the complete message stream,
   *  if it contains less messages.
   *
   *  @param   n the number of elements to drop
   *  @return  an input which produces all messages of the current input, except
   *           it omits the first `n` values.
   */
  // TODO

  /**
   * Skips longest sequence of elements of this input which satisfy given
   *  predicate `p`, and returns an input of the remaining elements.
   *
   *  @param p the predicate used to skip elements.
   *  @return  an input producing the remaining elements
   */
  def dropWhile(p: A => Boolean): Input[A]

  /**
   * Returns an input which groups messages produced by this input
   * into fixed size blocks. The last group may contain less messages
   * the number of messages receives is not a multiple of the group
   * size.
   *
   *  @param size the size of the groups.
   *  @return  an input producing the groups
   */
  def grouped(size: Int): Input[Seg[A]]

  /**
   * Forward the content of this input to an output. It returns when this input is
   * empty but does not close the output.
   *
   *  @param  output the output on which to forward this content
   *  @return the signal that closes this input
   */
  @deprecated("Use `flush` instead", "1.0")
  def forward(output: Output[A]): IO[Signal] =
    flush(output)

  /**
   * Flush the content of this input to an output. It returns when this input is
   * empty but does not close the output.
   *
   *  @param  output the output on which to forward this content
   *  @return the signal that closes this input
   */
  def flush(output: Output[A]): IO[Signal] =
    output.flush(this)
}

private[io] object Input {

  import impl.{ UThreadContext, Promise }

  private object InputReleasedSignal extends Signal
  private object InputOutdatedSignal extends Signal
  private object InputBusySignal extends Signal

  def apply[A: Message](master: UThreadContext, id: Int, ichan: IChan[A]): Input[A] = {
    if (ichan.isInstanceOf[NilIChan])
      new InputImpl[A](id, null, null, ichan)
    else {
      val ref = new MResourceRef(null)
      val input = new InputImpl[A](id, master, ref, ichan)
      ref.reset(input)
      master.context.add(ref)
      input
    }
  }

  // The fields 'master' and 'ref' should be 'val' but because of:
  // https://issues.scala-lang.org/browse/SI-5367
  // we must nullify them manually to release memory
  // when free inputs are unduly captured by closures.
  private[io] final class InputImpl[A: Message](
      val id: Int,
      private[this] final var master: UThreadContext,
      private[this] final var ref: MResourceRef,
      private[this] final var cnx: IChan[A]) extends Input[A] with Resource {

    def signal = cnx match {
      case IChan(signal) => Some(signal)
      case _ => None
    }

    /**
     * Transform this input into another input
     * @param reason signal that must be set on former input interface.
     *        It will be raise if someone attempts to read the former updated input.
     * @param isTransiant indicates if the update is temporary, in which case
     *        we should not clear resources acquired or referencing this Input.
     * @param f the transformer.
     */
    private def update[B: Message](reason: Signal, f: IChan[A] => IChan[B], isTransiant: Boolean): Input[B] = {
      cnx match {
        case IChan(signal) =>
          signal match {
            case InputReleasedSignal | InputOutdatedSignal | InputBusySignal =>
              sys.error("Input channel not available:" + signal)
            case _ =>
              // https://issues.scala-lang.org/browse/SI-5367
              new InputImpl[B](id, null, null, f(cnx))
          }
        case ichan =>
          cnx = IChan.empty(reason)
          f(ichan) match {
            case nil: NilIChan =>
              terminate(nil)
              // https://issues.scala-lang.org/browse/SI-5367
              new InputImpl[B](id, null, null, nil)
            case nichan =>
              if (isTransiant) {
                // Preserve resources because
                // this will be followed by a call to restore
                val in = Input(master, id, nichan)
                cnx = IChan.empty(InputBusySignal)
                in
              } else {
                // We are creating a new permanent input and can get rid 
                // of the former one.
                val in = new InputImpl(id, master, ref, nichan)
                ref.reset(in)
                clear(NilIChan(InputOutdatedSignal))
                in
              }

          }
      }
    }

    private[this] def terminate(nil: NilIChan): Unit = {
      if (ref == null) System.err.println("Warning: Input has been closed twice!")
      else {
        ref.reset(null)
        master.context.remove(ref)
        clear(nil)
      }
    }

    /**
     * This reduces damage of SI-5367, because this Input might be captured
     *  by the closure of the transformer and won't be garbage collected (e.g. prime sieve).
     */
    private[this] def clear(nil: NilIChan): Unit = {
      cnx = nil
      // https://issues.scala-lang.org/browse/SI-5367
      master = null
      ref = null
    }

    /**
     * Most transformations are like this.
     */
    def update[B: Message](f: IChan[A] => IChan[B]): Input[B] =
      update(InputOutdatedSignal, f, false)

    /**
     * This one is for temporary transformation like 'span'.
     * Contract: restore is called just before the temporary stream terminates.
     */
    def updateTemporarily[B >: A: Message](f: (IChan[A], IChan[A] => IChan[B]) => IChan[B]): Input[B] = {
      update(InputBusySignal, ichan => f(ichan, restore), true)
    }

    def restore: IChan[A] => IChan[Nothing] = {
      case nil: NilIChan =>
        terminate(nil)
        IChan.empty(EOS)
      case nchan =>
        cnx = nchan
        ref.reset(this)
        IChan.empty(EOS)
    }

    def extractSeg(t: UThreadContext, k: Seg[A] => Unit, klast: (Seg[A], Signal) => Unit): Unit = {
      cnx.read(t,
        { (seg, next) =>
          next match {
            case nil @ NilIChan(signal) =>
              terminate(nil)
              klast(seg, signal)
            case ichan =>
              cnx = ichan
              k(seg)
          }
        })
    }

    def extract(t: UThreadContext, k: A => Unit, h: Signal => Unit): Unit = {
      cnx match {
        case IChan(signal) =>
          h(signal)
        case ichan =>
          ichan.step(t) { (a, next) =>
            next match {
              case nil: NilIChan =>
                terminate(nil)
                k(a)
              case ichan =>
                cnx = ichan
                k(a)
            }
          } { signal =>
            terminate(NilIChan(signal))
            h(signal)
          }
      }
    }

    import parsing.{ ParseResult, Parser, Done, Partial, Fail }

    private def extractParse[B](t: UThreadContext, parser: Parser[A, B], k: B => Unit, h: Signal => Unit) {
      cnx match {
        case IChan(signal) =>
          h(signal)
        case ichan =>
          def loop(ichan: IChan[A], parser: Parser[A, B]) {
            ichan.read(t, { (seg, ichan) =>
              val parseResult: Option[ParseResult[B, A]] = ichan match {
                case IChan(signal) => parser.result(seg, signal) match {
                  case None => None
                  case Some(Left(fail)) => Some(Fail(fail.name, "Stream was aborted. " + fail.msg))
                  case Some(Right(done)) => Some(done)
                }
                case _ => Some(parser(seg))
              }
              parseResult match {
                case None =>
                  cnx = ichan
                  h(EOS)
                case Some(Done(b, as)) =>
                  cnx = as ++: ichan
                  k(b)
                case Some(Partial(parser)) =>
                  loop(ichan, parser)
                case Some(fail: Fail) =>
                  cnx = ichan
                  h(fail)
              }
            })
          }
          loop(ichan, parser)
        //    	  ichan.flatMap(parser, {(b:B, ichan:IChan[A]) => cnx = ichan; k(b); ichan}).read(t, (seg, next)  
      }
    }

    def read[C >: A, B](parser: parsing.Parser[C, B]): IO[B] = new IO[B]({ (t, k) =>
      extractParse(t, parser.asInstanceOf[parsing.Parser[A, B]], k, t.raise) // safe but how to do it cleanly?
    })

    def shutdown(signal: Signal) =
      poison_!(signal)

    private def poison_!(signal: Signal): Unit = {
      if (!cnx.isInstanceOf[NilIChan]) {
        // Input has not been already poisoned
        cnx.poison(signal)
        terminate(NilIChan(signal))
      }
    }

    def poison(signal: Signal): IO[Unit] = new IO[Unit]({ (t, k) =>
      poison_!(signal)
      k(())
    })

    def poison(): IO[Unit] = poison(EOS)

    import platform.UThread

    def test(thread: UThread): Promise[WInput[A]] = new Promise[WInput[A]]({ k =>
      val t = cnx.testable
      cnx = t
      t.test(thread, _ => k(this))
    })

    def release(): IO[IChan[A]] = new IO[IChan[A]]({ (t, k) =>
      cnx match {
        case nil @ IChan(signal) =>
          signal match {
            case InputReleasedSignal | InputOutdatedSignal | InputBusySignal =>
              t.raise(signal)
            case _ =>
              k(nil)
          }
        case ichan =>
          terminate(NilIChan(InputReleasedSignal))
          k(ichan)
      }
    })

    import channel.RIChan

    def connect(output: Output[A]): IO[RIChan[Either[Signal, Signal]]] =
      output.release() >>\ connect

    def connect(ochan: stream.OChan[A]): IO[RIChan[Either[Signal, Signal]]] =
      release() >>\ { ichan =>
        IO.launch(ichan connect ochan)(PureMessage)
      }

    def map[B: Message](f: A => B): Input[B] =
      update(_.map(f))

    def compress[B: Message](f: Seg[A] => B): Input[B] =
      update(_.compress(f))

    def debug(label: String, f: A => String = _.toString): Input[A] =
      update(_.debug(label, f))

    def collect[B: Message](f: PartialFunction[A, B]): Input[B] =
      update(_.collect(f))

    def flatMap[B: Message](f: A => Seg[B]): Input[B] =
      update(_.flatMapSeg(f))

    def smap[S, B: Message](z: S)(f: (S, A) => (S, B)): Input[B] =
      update(_.smap(z)(f))

    def parse[C >: A: Message, B: Message](parser: parsing.Parser[C, B]): Input[B] =
      update(_.parse(parser))

    def scan[B: Message](z: B)(f: (B, A) => B): Input[B] =
      update(_.scan(z)(f))

    def filter(p: A => Boolean): Input[A] =
      update(_.filter(p))

    def grouped(size: Int): Input[Seg[A]] =
      update(_.grouped(size))

    def flatten[B](implicit message: Message[B], asTraversable: A => /*<: Boolean): Input[A] =
      updateTemporarily(_.span(p, _))

    import stream.liftIChan
    def fold[B: Message](z: B)(f: (B, A) => B): IO[B] = new IO[B]({ (t, k) =>
      val ichan = cnx
      cnx = IChan.empty(InputBusySignal)
      val stream = ichan.fold(z)(f)
      val (ri, ro) = channel.RChan.mk[B]()
      stream.start(t, ro)
      liftIChan(ri).read(t, {
        case (Seg(b), _) => k(b)
        case (_, IChan(signal)) => t.raise(signal)
      })
    })

    def take(size: Int): Input[A] =
      updateTemporarily(_.take(size, _))

    def dropWhile(p: A => Boolean): Input[A] = {
      cnx = cnx.dropWhile(p)
      this
    }

    def +:[B >: A: Message](b: B): Input[B] =
      update(b :: _)

    def ++:[B >: A: Message](t: Seg[B]): Input[B] =
      update(t ++: _)

    def interleave[B](right: RInput[B])(implicit mb: Message[B]): IO[Input[Either[A, B]]] = for {
      i1 <- this.release()
      i2 <- right.release()
      i <- {
        val ilvd = i1.interleave(i2)
        IO.use(System.identityHashCode(ilvd), ilvd)
      }
    } yield i

    def merge[B >: A: Message](right: RInput[B]): IO[Input[B]] = for {
      i1 <- this.release()
      i2 <- right.release()
      i <- {
        val merged = i1.merge(i2)
        IO.use(System.identityHashCode(merged), merged)
      }
    } yield i

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy