Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/scala/eu/neverblink/jelly/cli/App.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ object App extends CommandsEntryPoint:
override def commands: Seq[Command[?]] = Seq(
Version,
RdfFromJelly,
RdfToJelly,
)
87 changes: 69 additions & 18 deletions src/main/scala/eu/neverblink/jelly/cli/JellyCommand.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package eu.neverblink.jelly.cli

import caseapp.*
import eu.neverblink.jelly.cli.util.IoUtil

import java.io.{ByteArrayOutputStream, OutputStream, PrintStream}
import java.io.*
import scala.compiletime.uninitialized

case class JellyOptions(
Expand All @@ -18,21 +19,25 @@ abstract class JellyCommand[T <: HasJellyOptions: {Parser, Help}] extends Comman
private var isDebug = false
final protected[cli] var out = System.out
final protected[cli] var err = System.err
final protected[cli] var in = System.in

private var osOut: ByteArrayOutputStream = uninitialized
private var osErr: ByteArrayOutputStream = uninitialized

/** Enable the "test mode" which captures stdout, stderr, exit code, and so on.
* @param test
* true to enable, false to disable
*/
def testMode(test: Boolean): Unit =
private def testMode(test: Boolean): Unit =
this.isTest = test
if test then
in = ByteArrayInputStream(Array())
osOut = ByteArrayOutputStream()
out = PrintStream(osOut)
osErr = ByteArrayOutputStream()
err = PrintStream(osErr)
else
in = System.in
out = System.out
err = System.err

Expand Down Expand Up @@ -76,31 +81,77 @@ abstract class JellyCommand[T <: HasJellyOptions: {Parser, Help}] extends Comman
osOut.reset()
osErr.reset()
App.main(args.toArray)
(osOut.toString, osErr.toString)
(osOut.toString("UTF-8"), osErr.toString("UTF-8"))

private def validateTestMode(): Unit =
if !isTest then throw new IllegalStateException("Not in test mode")

final def getOutString: String =
validateTestMode()
out.flush()
val s = osOut.toString
osOut.reset()
s

final def getOutBytes: Array[Byte] =
validateTestMode()
out.flush()
val b = osOut.toByteArray
osOut.reset()
b

final def getOutContent: String =
if isTest then
out.flush()
val s = osOut.toString
osOut.reset()
s
else throw new IllegalStateException("Not in test mode")
private final def getStdIn: InputStream =
if isTest then in
else System.in

final def setStdIn(data: ByteArrayInputStream): Unit =
validateTestMode()
in.reset()
in = data

final def getOutStream: OutputStream =
if isTest then osOut
else System.out

protected def getStdOut: OutputStream =
private def getStdOut: OutputStream =
if isTest then osOut
else System.out

final def getErrContent: String =
if isTest then
err.flush()
val s = osErr.toString
osErr.reset()
s
else throw new IllegalStateException("Not in test mode")
final def getErrString: String =
validateTestMode()
err.flush()
val s = osErr.toString
osErr.reset()
s

final def getErrBytes: Array[Byte] =
validateTestMode()
err.flush()
val b = osErr.toByteArray
osErr.reset()
b

/** This method matches the CLI input and output options to the correct file or standard
* input/output
* @param inputOption
* @param outputOption
* @return
*/
final def getIoStreamsFromOptions(
inputOption: Option[String],
outputOption: Option[String],
): (InputStream, OutputStream) =
val inputStream = inputOption match {
case Some(fileName: String) =>
IoUtil.inputStream(fileName)
case _ => getStdIn
}
val outputStream = outputOption match {
case Some(fileName: String) =>
IoUtil.outputStream(fileName)
case None => getStdOut
}
(inputStream, outputStream)

@throws[ExitException]
final override def exit(code: Int): Nothing =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import caseapp.*
import com.google.protobuf.InvalidProtocolBufferException
import eu.neverblink.jelly.cli.*
import eu.neverblink.jelly.cli.command.rdf.RdfFormatOption.*
import eu.neverblink.jelly.cli.util.IoUtil
import eu.ostrzyciel.jelly.convert.jena.riot.JellyLanguage
import eu.ostrzyciel.jelly.core.proto.v1.RdfStreamFrame
import eu.ostrzyciel.jelly.core.{IoUtils, RdfProtoDeserializationError}
Expand All @@ -13,10 +12,7 @@ import org.apache.jena.riot.{RDFLanguages, RDFParser, RiotException}
import java.io.{InputStream, OutputStream}

object RdfFromJellyPrint extends RdfCommandPrintUtil:
// We exclude JellyBinary because translating JellyBinary to JellyBinary makes no sense
override val validFormats: List[RdfFormatOption] =
RdfFormatOption.values.filterNot(_ == JellyBinary).toList

override val validFormats: List[RdfFormatOption] = List(JellyText, NQuads)
override val defaultFormat: RdfFormatOption = NQuads

case class RdfFromJellyOptions(
Expand All @@ -38,16 +34,8 @@ object RdfFromJelly extends JellyCommand[RdfFromJellyOptions]:
)

override def doRun(options: RdfFromJellyOptions, remainingArgs: RemainingArgs): Unit =
val inputStream = remainingArgs.remaining.headOption match {
case Some(fileName: String) =>
IoUtil.inputStream(fileName)
case _ => System.in
}
val outputStream = options.outputFile match {
case Some(fileName: String) =>
IoUtil.outputStream(fileName)
case None => getStdOut
}
val (inputStream, outputStream) =
this.getIoStreamsFromOptions(remainingArgs.remaining.headOption, options.outputFile)
doConversion(inputStream, outputStream, options.outputFormat)

/** This method takes care of proper error handling and matches the desired output format to the
Expand All @@ -58,8 +46,8 @@ object RdfFromJelly extends JellyCommand[RdfFromJellyOptions]:
* @param outputStream
* OutputStream
* @throws JellyDeserializationError
* @throws ParsingError
* @throws InvalidFormatSpecified
* @throws JenaRiotException
* @throws InvalidJellyFile
*/
private def doConversion(
inputStream: InputStream,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package eu.neverblink.jelly.cli.command.rdf
import caseapp.*
import com.google.protobuf.InvalidProtocolBufferException
import eu.neverblink.jelly.cli.*
import eu.neverblink.jelly.cli.command.rdf.RdfFormatOption.*
import eu.ostrzyciel.jelly.convert.jena.riot.JellyLanguage
import eu.ostrzyciel.jelly.core.RdfProtoSerializationError
import org.apache.jena.riot.system.StreamRDFWriter
import org.apache.jena.riot.{RDFLanguages, RDFParser, RiotException}

import java.io.{InputStream, OutputStream}

object RdfToJellyPrint extends RdfCommandPrintUtil:
override val validFormats: List[RdfFormatOption] = List(NQuads)
override val defaultFormat: RdfFormatOption = NQuads

case class RdfToJellyOptions(
@Recurse
common: JellyOptions = JellyOptions(),
@ExtraName("to") outputFile: Option[String] = None,
@ValueDescription("Input format.")
@HelpMessage(
RdfToJellyPrint.helpMsg,
)
@ExtraName("in-format") inputFormat: Option[String] = None,
) extends HasJellyOptions

object RdfToJelly extends JellyCommand[RdfToJellyOptions]:
override def group = "rdf"

override def names: List[List[String]] = List(
List("rdf", "to-jelly"),
)

override def doRun(options: RdfToJellyOptions, remainingArgs: RemainingArgs): Unit =
val (inputStream, outputStream) =
getIoStreamsFromOptions(remainingArgs.remaining.headOption, options.outputFile)
doConversion(inputStream, outputStream, options.inputFormat)

/** This method takes care of proper error handling and matches the desired output format to the
* correct conversion
*
* @param inputStream
* InputStream
* @param outputStream
* OutputStream
* @throws JellySerializationError
* @throws JenaRiotException
* @throws InvalidJellyFile
*/
private def doConversion(
inputStream: InputStream,
outputStream: OutputStream,
format: Option[String],
): Unit =
try {
format match {
case Some(f: String) =>
RdfFormatOption.find(f) match
case Some(NQuads) => nQuadToJelly(inputStream, outputStream)
case _ =>
throw InvalidFormatSpecified(
f,
RdfToJellyPrint.validFormatsString,
) // if anything else, it's an invalid option
case None =>
nQuadToJelly(inputStream, outputStream) // default option if no parameter supplied
}
} catch
case e: RdfProtoSerializationError =>
throw JellySerializationError(e.getMessage)
case e: RiotException =>
throw JenaRiotException(e)
case e: InvalidProtocolBufferException =>
throw InvalidJellyFile(e)

/** This method reads the NQuad file, rewrites it to Jelly and writes it to some output stream
* @param inputStream
* InputStream
* @param outputStream
* OutputStream
*/
private def nQuadToJelly(inputStream: InputStream, outputStream: OutputStream): Unit =
val jellyWriter = StreamRDFWriter.getWriterStream(outputStream, JellyLanguage.JELLY)
RDFParser.source(inputStream).lang(RDFLanguages.NQUADS).parse(jellyWriter)
Loading