This is an automated email from the ASF dual-hosted git repository.
aradzinski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/master by this push:
new 460bf6c WIP migration
460bf6c is described below
commit 460bf6c341be11307305d897fe7ce7846bd9e861
Author: Aaron Radzinski <[email protected]>
AuthorDate: Tue Oct 5 11:47:48 2021 -0700
WIP migration
---
.../nlpcraft/common/ansi/NCAnsiProgressBar.scala | 132 ++++++++++++++++++
.../nlpcraft/common/ansi/NCAnsiSpinner.scala | 112 +++++++++++++++
.../org/apache/nlpcraft/common/util/NCUtils.scala | 150 ++++++++++++++++++++-
3 files changed, 393 insertions(+), 1 deletion(-)
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
new file mode 100644
index 0000000..dc8e5f2
--- /dev/null
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiProgressBar.scala
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file 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
+ *
+ * https://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 org.apache.nlpcraft.common.ansi
+
+import java.io.PrintWriter
+import org.apache.nlpcraft.common._
+import NCAnsi._
+import org.apache.commons.lang3.StringUtils
+import org.apache.nlpcraft.common.ansi.NCAnsiProgressBar._
+
+/**
+ * Forward-only, bound ANSI-based progress bar.
+ *
+ * @param out
+ * @param totalTicks Number of ticks to complete.
+ * @param dispSize Visual size of the progress bar.
+ * @param clearOnComplete
+ * @param useAnsi
+ */
+class NCAnsiProgressBar(
+ out: PrintWriter,
+ totalTicks: Int,
+ dispSize: Int,
+ clearOnComplete: Boolean = true,
+ useAnsi: Boolean = true):
+ require(dispSize <= totalTicks)
+
+ @volatile private var tick = 0
+
+ private final val mux = new Object()
+ private final val PB_LEFT = s"$B${CHAR_SET.head}$RST"
+ private final val PB_RIGHT = s"$B${CHAR_SET(3)}$RST"
+ private final val PB_EMPTY = s"$W${CHAR_SET(2)}$RST"
+ private final val PB_FULL = s"$R$BO${CHAR_SET(1)}$RST"
+ private final val PB_LEAD = s"$Y$BO${CHAR_SET(4)}$RST"
+
+ /**
+ *
+ */
+ private def clean(): Unit =
+ out.print(ansiCursorLeft * (dispSize + 2/* Left & right brackets. */ +
5/* % string. */))
+ out.print(ansiClearLineAfter)
+ out.flush()
+
+ /**
+ * Starts progress bar.
+ */
+ def start(): Unit =
+ tick = 0
+ if useAnsi then
+ mux.synchronized {
+ // Hide cursor to avoid blinking.
+ out.print(ansiCursorHide)
+ out.print(PB_LEFT)
+ out.print(PB_EMPTY * dispSize)
+ out.print(PB_RIGHT)
+ out.print(" ")
+ out.print(s"${W}0% $RST")
+ out.flush()
+ }
+
+ /**
+ * Ticks progress bar one tick at a time.
+ */
+ def ticked(): Unit =
+ mux.synchronized {
+ tick += 1
+
+ if useAnsi then
+ clean()
+ val ratio = tick.toFloat / totalTicks.toFloat
+ val bar = if (tick == 1) 1 else Math.round(ratio * dispSize)
+ val pct = Math.round(ratio * 100)
+ out.print(PB_LEFT)
+ for (i <- 0 until dispSize) {
+ if (i < bar)
+ out.print(PB_FULL)
+ else if (i == bar)
+ out.print(PB_LEAD)
+ else
+ out.print(PB_EMPTY)
+ }
+ out.print(PB_RIGHT)
+ out.print(" ")
+ out.print(W + StringUtils.rightPad(s"$pct%",4) + RST)
+ out.flush()
+ else if tick == 1 || tick % (totalTicks / dispSize) == 0 then
+ out.print(NON_ANSI_CHAR)
+ out.flush()
+ }
+
+ /**
+ * Whether progress is complete.
+ *
+ * @return
+ */
+ def completed: Boolean = tick == totalTicks
+
+ /**
+ * Stops progress bar.
+ */
+ def stop(): Unit =
+ if useAnsi && clearOnComplete then mux.synchronized {
+ clean()
+
+ // Show cursor.
+ out.print(ansiCursorShow)
+ out.flush()
+ }
+
+/**
+ *
+ */
+object NCAnsiProgressBar:
+ // Active charset to use.
+ private final val NON_ANSI_CHAR = '='
+ private val CHAR_SET = Seq('[', '=', '.', ']', '>')
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
new file mode 100644
index 0000000..1a4bb4a
--- /dev/null
+++
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/ansi/NCAnsiSpinner.scala
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file 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
+ *
+ * https://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 org.apache.nlpcraft.common.ansi
+
+import java.io.PrintWriter
+import NCAnsi.*
+import org.apache.nlpcraft.common.*
+import org.apache.nlpcraft.common.ansi.NCAnsiSpinner.*
+import org.apache.nlpcraft.common.util.NCUtils
+
+/**
+ * ANSI-based hourglass spinner.
+ *
+ * @param out
+ * @param useAnsi
+ */
+class NCAnsiSpinner(out: PrintWriter, useAnsi: Boolean = true):
+ @volatile private var thread: Thread = _
+ @volatile private var suffix = ""
+ @volatile private var prefix = ""
+ @volatile private var lastLength = 0
+ @volatile private var frame = 0
+
+ private final val mux = new Object()
+
+ /**
+ *
+ * @param p
+ */
+ def setSuffix(p: String): Unit =
+ this.suffix = if (p == null) "" else p
+
+ /**
+ *
+ * @param p
+ */
+ def setPrefix(p: String): Unit =
+ this.prefix = if (p == null) "" else p
+
+ /**
+ *
+ */
+ private def clean(): Unit =
+ out.print(ansiCursorLeft * lastLength)
+ out.print(ansiClearLineAfter)
+ out.flush()
+
+ /**
+ * Starts spinner.
+ */
+ def start(): Unit =
+ if useAnsi then
+ thread = NCUtils.mkThread("ansi-spinner") { t =>
+ frame = 0
+ lastLength = 0
+ // Hide cursor to avoid blinking.
+ out.print(ansiCursorHide)
+ out.flush()
+ while (!t.isInterrupted)
+ mux.synchronized {
+ if frame > 0 then clean()
+ out.print(s"$prefix$ansiCyanFg${CHAR_SET(frame %
CHAR_SET.size)}$ansiReset$suffix")
+ out.flush()
+ }
+ lastLength = NCUtils.stripAnsi(prefix).length + 1 +
NCUtils.stripAnsi(suffix).length
+ frame += 1
+ Thread.sleep(1000 / CHAR_SET.size) // Full rotation per
second.
+ }
+
+ thread.start()
+ else
+ mux.synchronized {
+ out.print("... ")
+ out.flush()
+ }
+
+ /**
+ * Stops spinner.
+ */
+ def stop(): Unit =
+ NCUtils.stopThread(thread)
+
+ if useAnsi && frame > 0 then
+ mux.synchronized {
+ clean()
+
+ // Show cursor.
+ out.print(ansiCursorShow)
+ out.flush()
+ }
+
+/**
+ *
+ */
+object NCAnsiSpinner:
+ // An active charset to use.
+ private final val CHAR_SET = Seq('-', '\\', '|', '/')
diff --git
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 330cc94..f512afe 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -17,7 +17,8 @@
package org.apache.nlpcraft.common.util
-import com.typesafe.scalalogging.LazyLogging
+import com.typesafe.scalalogging.{LazyLogging, Logger}
+import org.apache.nlpcraft.common.NCException
import org.apache.nlpcraft.common.ansi.NCAnsi.*
import java.util.Random
@@ -411,3 +412,150 @@ object NCUtils extends LazyLogging:
sb.toString()
+ /**
+ *
+ * @param logger
+ * @param title
+ * @param e
+ */
+ def prettyError(logger: Logger, title: String, e: Throwable): Unit =
+ // Keep the full trace in the 'trace' log level.
+ logger.trace(title, e)
+
+ prettyErrorImpl(new PrettyErrorLogger {
+ override def log(s: String): Unit = logger.error(s)
+ }, title, e)
+
+
+ /**
+ *
+ * @param title
+ * @param e
+ */
+ def prettyError(title: String, e: Throwable): Unit = prettyErrorImpl(new
PrettyErrorLogger(), title, e)
+
+ sealed class PrettyErrorLogger:
+ def log(s: String): Unit = System.err.println(s)
+
+ /**
+ *
+ * @param logger
+ * @param title
+ * @param e
+ */
+ private def prettyErrorImpl(logger: PrettyErrorLogger, title: String, e:
Throwable): Unit =
+ logger.log(title)
+
+ val INDENT = 2
+ var x = e
+ var indent = INDENT
+ while (x != null)
+ var first = true
+ var errMsg = x.getLocalizedMessage
+ if errMsg == null then errMsg = "<null>"
+ val exClsName = if !x.isInstanceOf[NCException] then
s"$ansiRedFg[${x.getClass.getCanonicalName}]$ansiReset " else ""
+ val trace =
x.getStackTrace.find(!_.getClassName.startsWith("scala.")).getOrElse(x.getStackTrace.head)
+ val fileName = trace.getFileName
+ val lineNum = trace.getLineNumber
+ val msg =
+ if fileName == null || lineNum < 0 then
+ s"$exClsName$errMsg"
+ else
+ s"$exClsName$errMsg $ansiCyanFg->$ansiReset
($fileName:$lineNum)"
+
+ msg.split("\n").foreach(line => {
+ val s = s"${" " * indent}${if (first) ansiBlue("+-+ ") else "
"}${bo(y(line))}"
+ logger.log(s)
+ first = false
+ })
+
+ val traces = x.getStackTrace.filter { t =>
+ val mtdName = t.getMethodName
+ val clsName = t.getClassName
+
+ // Clean up trace.
+ clsName.startsWith("org.apache.nlpcraft") &&
+
!clsName.startsWith("org.apache.nlpcraft.common.opencensus") &&
+ !mtdName.contains("startScopedSpan") &&
+ !mtdName.contains('$')
+ }
+ for (trace <- traces)
+ val fileName = trace.getFileName
+ val lineNum = trace.getLineNumber
+ val mtdName = trace.getMethodName
+ val clsName =
trace.getClassName.replace("org.apache.nlpcraft", "o.a.n")
+
+ logger.log(s"${" " * indent} ${b("|")} $clsName.$mtdName
$ansiCyanFg->$ansiReset ($fileName:$lineNum)")
+
+ indent += INDENT
+
+ x = x.getCause
+
+ /**
+ * Makes thread.
+ *
+ * @param name Name.
+ * @param body Thread body.
+ */
+ def mkThread(name: String)(body: Thread => Unit): Thread =
+ new Thread(name):
+ @volatile private var stopped = false
+
+ override def isInterrupted: Boolean = super.isInterrupted ||
stopped
+ override def interrupt(): Unit = stopped = true; super.interrupt()
+
+ override def run(): Unit =
+ logger.trace(s"Thread started: $name")
+
+ try
+ body(this)
+ logger.trace(s"Thread exited: $name")
+
+ catch
+ case _: InterruptedException => logger.trace(s"Thread
interrupted: $name")
+ case e: Throwable => prettyError(logger, s"Unexpected
error during '$name' thread execution:", e)
+ finally
+ stopped = true
+
+ /**
+ * Makes thread.
+ *
+ * @param name Name.
+ * @param body Thread body.
+ */
+ def mkThread(name: String, body: Runnable): Thread =
+ mkThread(name) { _ => body.run() }
+
+ /**
+ * Sleeps number of milliseconds properly handling exceptions.
+ *
+ * @param delay Number of milliseconds to sleep.
+ */
+ def sleep(delay: Long): Unit =
+ try
+ Thread.sleep(delay)
+ catch
+ case _: InterruptedException => Thread.currentThread().interrupt()
+ case e: Throwable => prettyError(logger, "Unhandled exception
caught during sleep:", e)
+
+
+ /**
+ * Interrupts thread and waits for its finish.
+ *
+ * @param t Thread.
+ */
+ def stopThread(t: Thread): Unit =
+ if t != null then
+ t.interrupt()
+ try
+ t.join()
+ catch
+ case _: InterruptedException => logger.trace("Thread joining
was interrupted (ignoring).")
+
+ /**
+ * Interrupts thread.
+ *
+ * @param t Thread.
+ */
+ def interruptThread(t: Thread): Unit = if t != null then t.interrupt()
+