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

io.idml.repl.Repl.scala Maven / Gradle / Ivy

There is a newer version: 63488a16f994d8c0416bab30ec9ef2b0304a03b5
Show newest version
// scalastyle:off regex
package io.idmlrepl

import java.io.PrintWriter
import java.util.{Optional, Properties}

import io.idml.datanodes.IObject
import io.idml._
import io.idml.hashing.HashingFunctionResolver
import io.idml.jackson.IdmlJackson
import io.idml.jsoup.{IdmlJsoup, JsoupFunctionResolver}

import scala.sys.ShutdownHookThread
import scala.tools.jline.TerminalFactory
import scala.tools.jline.console.ConsoleReader
import scala.collection.JavaConverters._

/**
  * An IDML repl
  *
  * @author Stuart Dallas 
  */
class Repl {

  val build        = BuildInfo
  val IDML_VERSION = build.version
  val BUILD_DATE   = build.builtAtString

  val reader = new ConsoleReader()
  reader.getTerminal.setEchoEnabled(false)
  val out                  = new PrintWriter(reader.getOutput)
  var mode: String         = "json"
  var doc: Option[IObject] = None

  val idmlJson = IdmlJackson.default

  def run(args: Array[String]) = runInner(args)

  def runInner(args: Array[String], fr: Option[FunctionResolverService] = None) {
    addShutdownHook()

    val idml =
      new IdmlBuilder(Optional.of(fr.getOrElse(new FunctionResolverService))).build()

    out.println("""
        |idml %s (%s)
        |
        |Type ".help" for instructions on how to use this tool. Press ctrl+c to exit.
      """.stripMargin.format(IDML_VERSION, BUILD_DATE))
    out.println()

    try {
      while (mode != "quit") {
        var input = getInput(mode)
        if (input(0) == '.') {
          // It's a command. Tokenise the rest of the line.
          val tokens = input.substring(1).split("""\s+""")
          tokens(0).toLowerCase match {
            case "help"                  => displayHelp()
            case ("quit" | "exit" | "q") => mode = "quit"
            case "json"                  => mode = "json"
            case "idml"                  => mode = "idml"
            case "schema"                => mode = "schema"
            case "load" =>
              input = loadFile(input.substring(".load".length).trim)
              if (input.length > 0) {
                processInput(idml)(input)
              }
            case _ =>
              out.println("Error: Unknown command [" + tokens(0) + "]")
              ""
          }
        } else {
          processInput(idml)(input)
        }
        out.println()
      }
    } catch {
      // scalastyle:off null
      case e: Throwable if e.getMessage != null =>
        println("Fatal: " + e.getMessage)
      // scalastyle:on null
      case _: Throwable => None
    }
  }

  def addShutdownHook(): ShutdownHookThread = {
    sys.addShutdownHook({
      try {
        TerminalFactory.get().restore()
      } catch {
        // scalastyle:off null
        case e: Throwable if e.getMessage != null => println(e.getMessage)
        // scalastyle:on null
        case _: Throwable => None
      }
    })
  }

  def displayHelp() {
    out.println("""
                  |This REPL consists of several modes: json, idml, and schema. The current mode
                  |is identified by the prompt. In all modes you can simply type your input,
                  |ending with an empty line.
                  |
                  |In JSON mode you are entering the JSON document with which you want to work.
                  |Your JSON must consist of a single JSON document.
                  |
                  |In IDML mode you are entering the IDML mapping you want to run against the last
                  |JSON document entered.
                  |
                  |In Schema mode you are entering a schema you want to run against the last JSON
                  |document entered.
                  |
                  |At any prompt (but not at a continuation prompt) you can enter the following
                  |commands:
                  |
                  |  .help             Display this help message.
                  |  .quit/.q/.exit    Exit the REPL.
                  |  .json             Switch to JSON input mode.
                  |  .idml             Switch to IDML input mode.
                  |  .load   Use the specified file as input.
                  |  .schema           Switch to Schema input mode.
                """.stripMargin)
  }

  def processInput(idml: Idml)(input: String) {
    mode match {
      case "json"   => processJson(input)
      case "idml"   => processIdml(idml)(input)
      case "schema" => processSchema(idml)(input)
      case _        => out.println("Error: Unknown mode [" + mode + "]")
    }
  }

  def processJson(input: String) {
    // Store and parse the JSON.
    try {
      doc = Some(idmlJson.parse(input).asInstanceOf[IObject])
      out.println("JSON accepted")
      mode = "idml"
    } catch {
      case e: Throwable =>
        out.println("Error: " + e.getMessage)
    }
  }

  def processIdml(idml: Idml)(input: String) {
    try {
      val mapping = idml.compile(input)
      val output  = mapping.run(doc.get)
      out.println(idmlJson.pretty(output))
    } catch {
      case e: Throwable =>
        out.println("Error: " + e.getMessage)
    }
  }

  def processSchema(idml: Idml)(input: String) {
    try {
      var mapping: String = ""
      doc.get.fields.keySet.foreach {
        case key: String =>
          mapping += key + " = " + key + "\n"
      }
      val schema = idml.compile(mapping + input)
      val output = schema.run(doc.get)
      out.println(idmlJson.pretty(output))
    } catch {
      case e: Throwable =>
        out.println("Error: " + e.getMessage)
    }
  }

  def getInput(prompt: String): String = {
    var input = ""

    reader.setPrompt(prompt + "> ")
    while (input.trim.isEmpty) {
      input = reader.readLine().trim
    }

    if (input.charAt(0) != '.') {
      var line = ""
      reader.setPrompt(("." * prompt.length) + "> ")
      do {
        line = reader.readLine.trim
        input += "\n" + line
      } while (line.nonEmpty)
    }
    input
  }

  def loadFile(filename: String): String = {
    try {
      val source = scala.io.Source.fromFile(filename)
      val retval = source.getLines() mkString "\n"
      source.close()
      retval
    } catch {
      case e: Throwable =>
        out.println("Error: " + e.getMessage)
        ""
    }
  }
}
// scalastyle:on regex




© 2015 - 2024 Weber Informatics LLC | Privacy Policy