diff --git a/codemeta.json b/codemeta.json
index 8f405ce..30ab29d 100644
--- a/codemeta.json
+++ b/codemeta.json
@@ -135,18 +135,18 @@
"identifier" : "https://orcid.org/0000-0003-0765-7390"
},
"dateCreated" : "2018-02-22",
- "dateModified" : "2025-09-10",
+ "dateModified" : "2025-10-01",
"description" : "A heterogeneous data mapping language based on Shape Expressions",
"developmentStatus" : "active",
"downloadUrl" : "https://api.github.com/repos/herminiogg/ShExML/downloads",
- "identifier" : "https://doi.org/10.5281/zenodo.17092549",
+ "identifier" : "https://doi.org/10.5281/zenodo.17250444",
"license" : "https://api.github.com/licenses/mit",
"name" : "ShExML",
"programmingLanguage" : "Scala",
- "releaseNotes" : "## What's Changed\r\n- Added a parellelisation option in the RDF conversion. You can decide which parts of the execution you want to run in parallel and the number of threads to be used (or let the engine decide based on you hardware specs).\r\n- Stdin can be used as input for the mapping rules or as a input source.\r\n- Some minor fixes and stability improvements.\r\n\r\n**Full Changelog**: https://github.com/herminiogg/ShExML/compare/v0.5.4...v0.6.0",
+ "releaseNotes" : "## What's Changed\r\n* Fixed a bug preventing the correct processing of CSV files through the stdin.",
"runtimePlatform" : "JVM",
"softwareRequirements" : [ "http://example.org/jena-shacl", "http://example.org/Saxon-HE", "http://example.org/logback-classic", "http://example.org/jena-core", "http://example.org/scala-compiler", "http://example.org/mysql-connector-java", "http://example.org/stringdistance_2.13", "http://example.org/rmlmapper", "http://example.org/jena-base", "http://example.org/srdf_3", "http://example.org/scala-parallel-collections_3", "http://example.org/mssql-jdbc", "http://example.org/json-path", "http://example.org/picocli", "http://example.org/postgresql", "http://example.org/scala3-library_3", "http://example.org/shex_3", "http://example.org/scala-logging_3", "http://example.org/jena-arq", "http://example.org/srdf4j_3", "http://example.org/slf4j-nop", "http://example.org/antlr4", "http://example.org/mariadb-java-client", "http://example.org/sqlite-jdbc", "http://example.org/scalatest_3", "http://example.org/scala-csv_2.13", "http://example.org/scala-reflect" ],
- "version" : "0.6.0",
+ "version" : "0.6.1",
"issueTracker" : "https://api.github.com/repos/herminiogg/ShExML/issues",
"referencePublication" : "https://doi.org/10.7717/peerj-cs.318"
}
diff --git a/films.shexml b/films.shexml
index fb09425..87d28db 100644
--- a/films.shexml
+++ b/films.shexml
@@ -1,8 +1,8 @@
PREFIX :
PREFIX dbr:
PREFIX schema:
-SOURCE films_xml_file
-SOURCE films_json_file
+SOURCE films_xml_file
+SOURCE films_json_file
ITERATOR film_xml {
FIELD id <@id>
FIELD name
diff --git a/src/main/java/com/herminiogarcia/shexml/helper/LogRedErrorConverter.java b/src/main/java/com/herminiogarcia/shexml/helper/LogRedErrorConverter.java
new file mode 100644
index 0000000..1234edd
--- /dev/null
+++ b/src/main/java/com/herminiogarcia/shexml/helper/LogRedErrorConverter.java
@@ -0,0 +1,18 @@
+package com.herminiogarcia.shexml.helper;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.pattern.color.ANSIConstants;
+import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase;
+
+public class LogRedErrorConverter extends ForegroundCompositeConverterBase {
+
+ @Override
+ protected String getForegroundColorCode(ILoggingEvent event) {
+ if (event.getLevel().equals(Level.ERROR)) {
+ return ANSIConstants.RED_FG;
+ }
+ return ANSIConstants.DEFAULT_FG; // default color for everything else
+ }
+
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 402afa1..f0424bd 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -1,7 +1,10 @@
+
+
- %d{HH:mm:ss.SSS} %-5level[%thread] [%class] - %msg%n
+ %errorsInRed(%d{HH:mm:ss.SSS} %-5level[%thread] [%class] - %msg%n)
diff --git a/src/main/scala/com/herminiogarcia/shexml/Main.scala b/src/main/scala/com/herminiogarcia/shexml/Main.scala
index 22b56ba..4f66d73 100644
--- a/src/main/scala/com/herminiogarcia/shexml/Main.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/Main.scala
@@ -1,10 +1,11 @@
package com.herminiogarcia.shexml
-import com.herminiogarcia.shexml.helper.{ParallelExecutionConfigurator, PicocliLeftAlignedLayout}
+import com.herminiogarcia.shexml.helper.{ParallelExecutionConfigurator, PicocliLeftAlignedLayout, ShExMLError}
+import com.typesafe.scalalogging.Logger
import picocli.CommandLine
import picocli.CommandLine.{ArgGroup, Command, Option}
-import java.io.{File, PrintWriter}
+import java.io.{File, PrintWriter, StringWriter}
import java.util.concurrent.Callable
/**
@@ -26,6 +27,8 @@ object Main {
description = Array("Map and merge heterogeneous data sources with a Shape Expressions based syntax"))
class Main extends Callable[Int] {
+ private val logger = Logger[MappingLauncher]
+
@Option(names = Array("-m", "--mapping"), required = true, description = Array("Path to the file with the mappings. If '-' is provided as the path the engine will read from the standard input."))
private var file: String = ""
@@ -43,8 +46,8 @@ class Main extends Callable[Int] {
val parallelExecutionConfiguration =
if(generalTransformationOptions.parallel) ParallelExecutionConfigurator(generalTransformationOptions.parallelAspects, generalTransformationOptions.numberOfThreads)
else ParallelExecutionConfigurator.empty
+ val fileContent = fileHandler.mkString
try {
- val fileContent = fileHandler.mkString
val mappingLauncher = new MappingLauncher(
generalTransformationOptions.username,
generalTransformationOptions.password,
@@ -76,6 +79,15 @@ class Main extends Callable[Int] {
pw.close()
}
0 // well finished
+ } catch {
+ case e: ShExMLError =>
+ val sw = new StringWriter()
+ val pw = new PrintWriter(sw)
+ e.printStackTrace(pw)
+ val stackTrace = sw.toString
+ pw.close(); sw.close()
+ logger.error(s"${e.getEnrichedErrorMessage(fileContent)}\n$stackTrace")
+ 1 // badly finished
} finally { fileHandler.close() }
}
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/MappingLauncher.scala b/src/main/scala/com/herminiogarcia/shexml/MappingLauncher.scala
index 70b1c0d..be7917a 100644
--- a/src/main/scala/com/herminiogarcia/shexml/MappingLauncher.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/MappingLauncher.scala
@@ -2,14 +2,15 @@ package com.herminiogarcia.shexml
import com.herminiogarcia.shexml.antlr.{ShExMLLexer, ShExMLParser}
import com.herminiogarcia.shexml.ast._
-import com.herminiogarcia.shexml.helper.{OrphanBNodeRemover, ParallelExecutionConfigurator, SourceHelper}
+import com.herminiogarcia.shexml.helper.{LexerErrorListener, OrphanBNodeRemover, ParallelExecutionConfigurator, ParserErrorListener, SourceHelper}
import com.herminiogarcia.shexml.parser.ASTCreatorVisitor
import com.herminiogarcia.shexml.shex._
-import com.herminiogarcia.shexml.visitor.{PushedOrPoppedValueSearchVisitor, RDFGeneratorVisitor, RMLGeneratorVisitor, VarTableBuilderVisitor}
+import com.herminiogarcia.shexml.visitor.{PushedOrPoppedValueSearchVisitor, RDFGeneratorVisitor, RMLGeneratorVisitor, SemanticCheckerVisitor, VarTableBuilderVisitor}
import org.antlr.v4.runtime.{CharStreams, CommonTokenStream}
import org.apache.jena.query.{Dataset, DatasetFactory}
import org.apache.jena.riot.{RDFDataMgr, RDFFormat, RDFLanguages}
import com.typesafe.scalalogging.Logger
+
import java.io.ByteArrayOutputStream
import scala.collection.JavaConverters._
import java.util.concurrent.ConcurrentLinkedQueue
@@ -47,6 +48,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val parser = createParser(lexer)
val ast = createAST(parser)
val varTable = createVarTable(ast)
+ runSemanticAnalysis(ast, varTable)
generateResultingRDF(ast, varTable)
}
@@ -58,6 +60,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val ast = createAST(parser)
val varTable = createVarTable(ast)
val dataset = generateResultingRML(ast, varTable, prettify)
+ runSemanticAnalysis(ast, varTable)
val outputStream = new ByteArrayOutputStream()
if(prettify) new OrphanBNodeRemover().removeOrphanBNodes(dataset.getDefaultModel)
RDFDataMgr.write(outputStream, dataset.getDefaultModel, RDFFormat.TURTLE_PRETTY)
@@ -71,6 +74,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val parser = createParser(lexer)
val ast = createAST(parser)
val varTable = createVarTable(ast)
+ runSemanticAnalysis(ast, varTable)
val inferencesTable = new ConcurrentLinkedQueue[ShExMLInferredCardinalitiesAndDatatypes]()
generateInferencesFromShExML(ast, varTable, inferencesTable)
val shex = new ShExGeneratorVisitor(inferencesTable.asScala.toList).doVisit(ast, null)
@@ -84,6 +88,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val parser = createParser(lexer)
val ast = createAST(parser)
val varTable = createVarTable(ast)
+ runSemanticAnalysis(ast, varTable)
val shapeMaps = generateShapeMaps(ast, varTable)
new ShapeMapPrinter().print(shapeMaps)
}
@@ -96,6 +101,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val ast = createAST(parser)
val varTable = createVarTable(ast)
val inferencesTable = new ConcurrentLinkedQueue[ShExMLInferredCardinalitiesAndDatatypes]()
+ runSemanticAnalysis(ast, varTable)
generateInferencesFromShExML(ast, varTable, inferencesTable)
logger.info(s"Executing ShEx extraction process as base for the SHACL conversion")
val shex = new ShExGeneratorVisitor(inferencesTable.asScala.toList).doVisit(ast, null)
@@ -115,6 +121,8 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
val lexer = createLexer(mappingCode)
val parser = createParser(lexer)
val ast = createAST(parser)
+ val varTable = createVarTable(ast)
+ runSemanticAnalysis(ast, varTable)
precompiledMappingRules
} catch {
case e: Exception =>
@@ -126,12 +134,18 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
private def createLexer(mappingCode: String): ShExMLLexer = {
val finalMappingRules = resolveImports(mappingCode)
logger.info("Applying lexer to tokenize input mapping rules")
- new ShExMLLexer(CharStreams.fromString(finalMappingRules))
+ val lexer = new ShExMLLexer(CharStreams.fromString(finalMappingRules))
+ lexer.removeErrorListeners()
+ lexer.addErrorListener(new LexerErrorListener)
+ lexer
}
private def createParser(lexer: ShExMLLexer): ShExMLParser = {
logger.info("Parsing tokens from the lexer")
- new ShExMLParser(new CommonTokenStream(lexer))
+ val parser = new ShExMLParser(new CommonTokenStream(lexer))
+ parser.removeErrorListeners()
+ parser.addErrorListener(new ParserErrorListener)
+ parser
}
private def createAST(parser: ShExMLParser): AST = {
@@ -142,7 +156,7 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
private def createVarTable(ast: AST): mutable.HashMap[Variable, VarResult] = {
logger.info(s"Building var table")
val varTable = mutable.HashMap[Variable, VarResult]()
- varTable += ((Var("rdf:"), URL("http://www.w3.org/1999/02/22-rdf-syntax-ns#")))
+ varTable += ((Var("rdf:"), URL("http://www.w3.org/1999/02/22-rdf-syntax-ns#", UnknownParserInfo)))
val optionalArgument = Map("variable" -> "", "query" -> "")
new VarTableBuilderVisitor(varTable).visit(ast, optionalArgument)
logger.debug(s"Var table contents:")
@@ -150,6 +164,12 @@ class MappingLauncher(val username: String = "", val password: String = "", driv
varTable
}
+ private def runSemanticAnalysis(ast: AST, varTable: mutable.HashMap[Variable, VarResult]): Unit = {
+ logger.info(s"Running semantic analysis")
+ new SemanticCheckerVisitor(varTable.toMap).doVisit(ast, null)
+ logger.debug(s"Semantic analysis completed satisfactorily")
+ }
+
private def generateResultingRDF(ast: AST, varTable: mutable.HashMap[Variable, VarResult]): Dataset = {
val dataset = DatasetFactory.create()
diff --git a/src/main/scala/com/herminiogarcia/shexml/ast/AST.scala b/src/main/scala/com/herminiogarcia/shexml/ast/AST.scala
index 6cfcded..0248145 100644
--- a/src/main/scala/com/herminiogarcia/shexml/ast/AST.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/ast/AST.scala
@@ -3,30 +3,32 @@ package com.herminiogarcia.shexml.ast
/**
* Created by herminio on 20/12/17.
*/
-sealed trait AST
+sealed trait AST {
+ val parserInfo: ParserInfo
+}
-case class ShExML(declaration: List[Declaration], graph: List[Graph], shape: List[Shape]) extends AST
-case class Declaration(declarationStatement: DeclarationStatement) extends AST
-case class Graph(graphName: GraphVar, shapes: List[Shape]) extends VarResult
-case class Shape(shapeName: ShapeVar, action: ActionOrLiteral, predicateObjects: List[PredicateObject], holdingGraph: Option[Graph]) extends VarResult
+case class ShExML(declaration: List[Declaration], graph: List[Graph], shape: List[Shape], parserInfo: ParserInfo) extends AST
+case class Declaration(declarationStatement: DeclarationStatement, parserInfo: ParserInfo) extends AST
+case class Graph(graphName: GraphVar, shapes: List[Shape], parserInfo: ParserInfo) extends VarResult
+case class Shape(shapeName: ShapeVar, action: ActionOrLiteral, predicateObjects: List[PredicateObject], holdingGraph: Option[Graph], parserInfo: ParserInfo) extends VarResult
sealed trait ActionOrLiteral extends AST
-case class Action(shapePrefix: String, action: ExpOrVar, condition: Option[ExpOrVar]) extends ActionOrLiteral
-case class LiteralSubject(prefix: Var, value: String) extends ActionOrLiteral
+case class Action(shapePrefix: Var, action: ExpOrVar, condition: Option[ExpOrVar], parserInfo: ParserInfo) extends ActionOrLiteral
+case class LiteralSubject(prefix: Var, value: String, parserInfo: ParserInfo) extends ActionOrLiteral
sealed trait DeclarationStatement extends AST
-case class Prefix(name: Var, url: URL) extends DeclarationStatement
-case class Source(name: Var, path: FilePathOrStdin) extends DeclarationStatement
-case class Query(name: Var, query: QueryOrURL) extends DeclarationStatement
-case class Functions(name: Var, query: FilePath) extends DeclarationStatement
-case class Iterator(name: Var, queryClause: QueryOrVar, fields: List[Field], iterators: List[NestedIterator]) extends Iterators with DeclarationStatement with VarResult
-case class NestedIterator(name: Var, queryClause: QueryClause, fields: List[Field], iterators: List[NestedIterator]) extends Iterators with DeclarationStatement with VarResult
-case class Field(name: Var, queryClause: QueryClause, pushed: Boolean, popped: Boolean) extends AST
-case class Expression(name: Var, exp: Exp) extends DeclarationStatement
-case class Matcher(replacedStrings: ReplacedStrings, replacement: String) extends AST
-case class Matchers(name: Var, matchers: MatcherList) extends DeclarationStatement with VarResult
-case class AutoIncrement(name: Var, from: Int, to: Int, by: Int, precedentString: Option[String], closingString: Option[String]) extends Iterable[Int] with DeclarationStatement with VarResult {
+case class Prefix(name: Var, url: URL, parserInfo: ParserInfo) extends DeclarationStatement
+case class Source(name: Var, path: FilePathOrStdin, parserInfo: ParserInfo) extends DeclarationStatement
+case class Query(name: Var, query: QueryOrURL, parserInfo: ParserInfo) extends DeclarationStatement
+case class Functions(name: Var, query: FilePath, parserInfo: ParserInfo) extends DeclarationStatement
+case class Iterator(name: Var, queryClause: QueryOrVar, fields: List[Field], iterators: List[NestedIterator], parserInfo: ParserInfo) extends Iterators with DeclarationStatement with VarResult
+case class NestedIterator(name: Var, queryClause: QueryClause, fields: List[Field], iterators: List[NestedIterator], parserInfo: ParserInfo) extends Iterators with DeclarationStatement with VarResult
+case class Field(name: Var, queryClause: QueryClause, pushed: Boolean, popped: Boolean, parserInfo: ParserInfo) extends AST
+case class Expression(name: Var, exp: Exp, parserInfo: ParserInfo) extends DeclarationStatement
+case class Matcher(replacedStrings: ReplacedStrings, replacement: String, parserInfo: ParserInfo) extends AST
+case class Matchers(name: Var, matchers: MatcherList, parserInfo: ParserInfo) extends DeclarationStatement with VarResult
+case class AutoIncrement(name: Var, from: Int, to: Int, by: Int, precedentString: Option[String], closingString: Option[String], parserInfo: ParserInfo) extends Iterable[Int] with DeclarationStatement with VarResult {
override val iterator: scala.Iterator[Int] = scala.Iterator.range(from, to, by)
}
@@ -36,26 +38,26 @@ sealed trait QueryClause extends QueryOrURL with QueryOrVar {
val popped: Boolean = false
}
-case class JsonPath(query: String) extends QueryClause
-case class XmlPath(query: String) extends QueryClause
-case class CSVPerRow(query: String) extends QueryClause
+case class JsonPath(query: String, parserInfo: ParserInfo) extends QueryClause
+case class XmlPath(query: String, parserInfo: ParserInfo) extends QueryClause
+case class CSVPerRow(query: String, parserInfo: ParserInfo) extends QueryClause
sealed trait Sql extends QueryClause
-case class SqlQuery(query: String) extends Sql
-case class SqlColumn(query: String, column: String) extends Sql
+case class SqlQuery(query: String, parserInfo: ParserInfo) extends Sql
+case class SqlColumn(query: String, column: String, parserInfo: ParserInfo) extends Sql
sealed trait Sparql extends QueryClause
-case class SparqlQuery(query: String) extends Sparql
-case class SparqlColumn(query: String, column: String) extends Sparql
-case class FieldQuery(query: String, override val pushed: Boolean = false, override val popped: Boolean = false) extends QueryClause
+case class SparqlQuery(query: String, parserInfo: ParserInfo) extends Sparql
+case class SparqlColumn(query: String, column: String, parserInfo: ParserInfo) extends Sparql
+case class FieldQuery(query: String, override val pushed: Boolean = false, override val popped: Boolean = false, parserInfo: ParserInfo) extends QueryClause
sealed trait Exp extends ExpOrVar with VarResult
sealed trait LeftUnion extends Exp
sealed trait RightUnion extends Exp
-case class Union(left: LeftUnion, right: RightUnion) extends RightUnion
-case class StringOperation(left: IteratorQuery, right: IteratorQuery, unionString: String) extends LeftUnion with RightUnion
-case class Substitution(leftUnion: IteratorQuery, rightUnion: IteratorQuery, joinClause: IteratorQuery) extends Exp
-case class Join(leftUnion: IteratorQuery, rightUnion: IteratorQuery, leftJoinClause: IteratorQuery, rightJoinClause: IteratorQuery) extends Exp
-case class IteratorQuery(firstVar: Var, composedVar: VarOrIteratorQuery, builtinFunction: Option[BuiltinFunction] = None) extends LeftUnion with RightUnion with VarOrIteratorQuery
+case class Union(left: LeftUnion, right: RightUnion, parserInfo: ParserInfo) extends RightUnion
+case class StringOperation(left: IteratorQuery, right: IteratorQuery, unionString: String, parserInfo: ParserInfo) extends LeftUnion with RightUnion
+case class Substitution(leftUnion: IteratorQuery, rightUnion: IteratorQuery, joinClause: IteratorQuery, parserInfo: ParserInfo) extends Exp
+case class Join(leftUnion: IteratorQuery, rightUnion: IteratorQuery, leftJoinClause: IteratorQuery, rightJoinClause: IteratorQuery, parserInfo: ParserInfo) extends Exp
+case class IteratorQuery(firstVar: Var, composedVar: VarOrIteratorQuery, builtinFunction: Option[BuiltinFunction] = None, parserInfo: ParserInfo) extends LeftUnion with RightUnion with VarOrIteratorQuery
sealed trait VarOrIteratorQuery extends AST
sealed trait ExpOrVar extends AST
@@ -63,32 +65,50 @@ sealed trait QueryOrURL extends VarResult
sealed trait QueryOrVar extends VarResult
sealed trait Variable extends ExpOrVar
-case class Var(name: String) extends Variable with VarOrIteratorQuery with QueryOrVar
-case class ShapeVar(name: String) extends Variable
-case class GraphVar(prefix: String, name: String) extends Variable
+case class Var(name: String, parserInfo: ParserInfo = UnknownParserInfo) extends Variable with VarOrIteratorQuery with QueryOrVar {
+ override def equals(obj: Any): Boolean = obj match {
+ case v: Var => v.name == name
+ case _ => false
+ }
+ override def hashCode(): Int = name.hashCode
+}
+case class ShapeVar(name: String, parserInfo: ParserInfo) extends Variable {
+ override def equals(obj: Any): Boolean = obj match {
+ case v: ShapeVar => v.name == name
+ case _ => false
+ }
+ override def hashCode(): Int = name.hashCode
+}
+case class GraphVar(prefix: String, name: String, parserInfo: ParserInfo) extends Variable {
+ override def equals(obj: Any): Boolean = obj match {
+ case v: GraphVar => v.prefix == prefix && v.name == name
+ case _ => false
+ }
+ override def hashCode(): Int = (prefix, name).hashCode
+}
-case class LiteralObject(prefix: Var, value: String) extends ObjectOrShapeLink
-case class LiteralObjectValue(value: String) extends ObjectOrShapeLink
+case class LiteralObject(prefix: Var, value: String, parserInfo: ParserInfo) extends ObjectOrShapeLink
+case class LiteralObjectValue(value: String, parserInfo: ParserInfo) extends ObjectOrShapeLink
-case class PredicateObject(predicate: Predicate, objectOrShapeLink: ObjectOrShapeLink) extends AST
-case class Predicate(prefix: String, extension: String) extends AST
+case class PredicateObject(predicate: Predicate, objectOrShapeLink: ObjectOrShapeLink, parserInfo: ParserInfo) extends AST
+case class Predicate(prefix: String, extension: String, parserInfo: ParserInfo) extends AST
-case class FunctionCalling(functionHub: Var, functionName: Var, arguments: Arguments) extends ExpOrVar
-case class Arguments(arguments: List[ExpOrVar]) extends AST
+case class FunctionCalling(functionHub: Var, functionName: Var, arguments: Arguments, parserInfo: ParserInfo) extends ExpOrVar
+case class Arguments(arguments: List[ExpOrVar], parserInfo: ParserInfo) extends AST
sealed trait ObjectOrShapeLink extends AST
-case class ObjectElement(prefix: String, action: Option[ExpOrVar], literalValue: Option[LiteralObjectValue], matcher: Option[Var], filter: Option[ExpOrVar],
- dataType: Option[DataType], langTag: Option[LangTag], rdfCollection: Option[RDFCollection]) extends ObjectOrShapeLink
-case class ShapeLink(shape: ShapeVar) extends ObjectOrShapeLink
+case class ObjectElement(prefix: Option[Var], action: Option[ExpOrVar], literalValue: Option[LiteralObjectValue], matcher: Option[Var], filter: Option[ExpOrVar],
+ dataType: Option[DataType], langTag: Option[LangTag], rdfCollection: Option[RDFCollection], parserInfo: ParserInfo) extends ObjectOrShapeLink
+case class ShapeLink(shape: ShapeVar, parserInfo: ParserInfo) extends ObjectOrShapeLink
sealed trait DataType extends AST
-case class DataTypeGeneration(prefix: String, action: ExpOrVar, matcher: Option[Var]) extends DataType
-case class DataTypeLiteral(value: String) extends DataType
+case class DataTypeGeneration(prefix: Option[Var], action: ExpOrVar, matcher: Option[Var], parserInfo: ParserInfo) extends DataType
+case class DataTypeLiteral(value: String, parserInfo: ParserInfo) extends DataType
sealed trait LangTag extends AST
-case class LangTagGeneration(action: ExpOrVar, matcher: Option[Var]) extends LangTag
-case class LangTagLiteral(value: String) extends LangTag
+case class LangTagGeneration(action: ExpOrVar, matcher: Option[Var], parserInfo: ParserInfo) extends LangTag
+case class LangTagLiteral(value: String, parserInfo: ParserInfo) extends LangTag
sealed trait VarResult extends AST
sealed trait Iterators extends AST {
@@ -103,19 +123,22 @@ sealed trait FilePathOrStdin extends VarResult
sealed trait FilePath extends FilePathOrStdin {
def value: String
}
-case class URL(value: String) extends FilePath with QueryOrURL
-case class JdbcURL(value: String) extends FilePath
-case class RelativePath(value: String) extends FilePath
-case class Stdin() extends FilePathOrStdin
-case class ReplacedStrings(strings: List[String]) extends AST
-case class ComposedVariable(variables: List[Var]) extends AST
-case class MatcherList(matchers: List[Matcher]) extends AST
+case class URL(value: String, parserInfo: ParserInfo) extends FilePath with QueryOrURL
+case class JdbcURL(value: String, parserInfo: ParserInfo) extends FilePath
+case class RelativePath(value: String, parserInfo: ParserInfo) extends FilePath
+case class Stdin(parserInfo: ParserInfo) extends FilePathOrStdin
+case class ReplacedStrings(strings: List[String], parserInfo: ParserInfo) extends AST
+case class ComposedVariable(variables: List[Var], parserInfo: ParserInfo) extends AST
+case class MatcherList(matchers: List[Matcher], parserInfo: ParserInfo) extends AST
sealed trait RDFCollection extends AST
-case class RDFList() extends RDFCollection
-case class RDFBag() extends RDFCollection
-case class RDFAlt() extends RDFCollection
-case class RDFSeq() extends RDFCollection
+case class RDFList(parserInfo: ParserInfo) extends RDFCollection
+case class RDFBag(parserInfo: ParserInfo) extends RDFCollection
+case class RDFAlt(parserInfo: ParserInfo) extends RDFCollection
+case class RDFSeq(parserInfo: ParserInfo) extends RDFCollection
sealed trait BuiltinFunction extends AST
-case class Index() extends BuiltinFunction
\ No newline at end of file
+case class Index(parserInfo: ParserInfo) extends BuiltinFunction
+
+class ParserInfo(val startLine: Option[Int], val startColumn: Option[Int], val endLine: Option[Int], val endColumn: Option[Int])
+object UnknownParserInfo extends ParserInfo(startLine = None, startColumn = None, endLine = None, endColumn = None)
diff --git a/src/main/scala/com/herminiogarcia/shexml/helper/FunctionHubExecutor.scala b/src/main/scala/com/herminiogarcia/shexml/helper/FunctionHubExecutor.scala
index 4e40b7d..a058bbd 100644
--- a/src/main/scala/com/herminiogarcia/shexml/helper/FunctionHubExecutor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/helper/FunctionHubExecutor.scala
@@ -4,6 +4,7 @@ import com.github.vickumar1981.stringdistance
import com.github.vickumar1981.stringdistance.StringDistance.Levenshtein
import com.github.vickumar1981.stringdistance.implicits._
import com.github.vickumar1981.stringdistance.StringConverter._
+import com.herminiogarcia.shexml.ast.ParserInfo
import com.typesafe.scalalogging.Logger
import scala.tools.reflect.ToolBox
@@ -11,7 +12,7 @@ import scala.reflect.runtime._
import scala.reflect.runtime
import scala.reflect.runtime.universe._
-class FunctionHubExecutor(val functionsCode: LoadedSource) {
+class FunctionHubExecutor(val functionsCode: LoadedSource, val parserInfo: ParserInfo) {
private val cm = universe.runtimeMirror(getClass.getClassLoader)
private val toolBox = cm.mkToolBox()
@@ -29,7 +30,9 @@ class FunctionHubExecutor(val functionsCode: LoadedSource) {
name.levenshteinDist(a.getName) < name.levenshteinDist(b.getName)
//Levenshtein.distance(name, a.getName)(LevenshteinDistance) < Levenshtein.distance(name, b.getName)
})
- .head
+ .headOption.getOrElse(
+ throw RDFGenerationError(s"Function $name was not found in source code ${functionsCode.filepath}", parserInfo)
+ )
val parameterTypes = method.getParameterTypes.toSeq
val finalArgs = for((arg, theType) <- args zip parameterTypes) yield {
val typeName =
@@ -59,7 +62,7 @@ class FunctionHubExecutor(val functionsCode: LoadedSource) {
else if(typeName == "Short") classOf[Short]
else if(typeName == "Byte") classOf[Byte]
else if(typeName == "java.lang.String") classOf[String]
- else throw new Exception(typeName + "is not a supported type")
+ else throw FunctionExecutionError(typeName + "is not a supported type")
}
}
\ No newline at end of file
diff --git a/src/main/scala/com/herminiogarcia/shexml/helper/ShExMLError.scala b/src/main/scala/com/herminiogarcia/shexml/helper/ShExMLError.scala
new file mode 100644
index 0000000..cdb2da8
--- /dev/null
+++ b/src/main/scala/com/herminiogarcia/shexml/helper/ShExMLError.scala
@@ -0,0 +1,96 @@
+package com.herminiogarcia.shexml.helper
+
+import com.herminiogarcia.shexml.ast.{ParserInfo, UnknownParserInfo}
+import org.antlr.v4.runtime.{BaseErrorListener, CommonToken, RecognitionException, Recognizer}
+
+import scala.collection.JavaConverters._
+
+trait ShExMLError extends Exception {
+ val message: String
+ val parserInfo: ParserInfo
+ private val BOLD = "\u001B[1m"
+ private val UNDERLINE = "\u001B[4m"
+ private val RESET_BOLD = "\u001B[22m"
+ private val RESET_UNDERLINE = "\u001B[24m"
+
+ def getEnrichedErrorMessage: String = {
+ s"$message between line ${parserInfo.startLine.getOrElse("unknown")} column ${parserInfo.endColumn.getOrElse("unknown")} and line ${parserInfo.endLine.getOrElse("unknown")} column ${parserInfo.endColumn.getOrElse("unknown")}"
+ }
+
+ def getEnrichedErrorMessage(input: String): String = {
+ val inputLines = input.lines().toList.asScala.toList
+ parserInfo.startLine match {
+ case Some(sl) if sl > 0 =>
+ parserInfo.endLine.flatMap(el => {
+ if(el == sl) {
+ val mainLine = inputLines(sl - 1)
+ parserInfo.startColumn.map(sc => {
+ val mainLineSplit = mainLine.splitAt(sc)
+ val endSplit = parserInfo.endColumn.map(ec => mainLineSplit._2.splitAt((ec - 1) - (mainLineSplit._1.length - 1)))
+ val highlightedLine = endSplit.map(es => generateHighlightedErrorExtract(sl, mainLineSplit._1, es._1, es._2))
+ s"$message in line $sl between columns ${parserInfo.startColumn.getOrElse("unknown")} and ${parserInfo.endColumn.getOrElse("unknown")}\n\n${highlightedLine.getOrElse("")}\n"
+ })
+ } else {
+ val mainLines = inputLines.slice(sl - 1 , el)
+ parserInfo.startColumn.map(sc => {
+ val firstLineSplit = mainLines.head.splitAt(sc)
+ val lastLineSplit = parserInfo.endColumn.map(ec => mainLines.last.splitAt(ec))
+ val inBetweenLines = {
+ val lines = mainLines.tail.dropRight(1).mkString("\n")
+ if(lines.nonEmpty) lines + "\n"
+ else lines
+ }
+ val highlightedLines = lastLineSplit.map(lls => generateHighlightedErrorExtract(sl, firstLineSplit._1, firstLineSplit._2 + "\n" + inBetweenLines + lls._1, lls._2))
+ s"$message between line $sl column ${parserInfo.startColumn.getOrElse("unknown")} and line $el and column ${parserInfo.endColumn.getOrElse("unknown")}\n\n${highlightedLines.getOrElse("")}\n"
+ })
+ }
+ }).getOrElse(s"$message near line $sl and column ${parserInfo.startColumn.getOrElse("unknown")}\n")
+ case None =>
+ this.getEnrichedErrorMessage
+ }
+ }
+
+ private def generateHighlightedErrorExtract(firstLineNumber: Int, before: String, affectedFragment: String, after: String): String = {
+ val lineCounter = Iterator.from(firstLineNumber)
+ val output = before + BOLD + UNDERLINE + affectedFragment + RESET_BOLD + RESET_UNDERLINE + after
+ output.lines().toList.asScala.map(ol => s"${lineCounter.next()}: ${resetBoldAndUnderline(ol)}").mkString("\n")
+ }
+
+ private def resetBoldAndUnderline(input: String): String = {
+ if(input.contains(BOLD + UNDERLINE) && !input.contains(RESET_BOLD + RESET_UNDERLINE)) {
+ input + RESET_BOLD + RESET_UNDERLINE
+ } else if(!input.contains(BOLD + UNDERLINE) && input.contains(RESET_BOLD + RESET_UNDERLINE)) {
+ BOLD + UNDERLINE + input
+ } else input
+ }
+}
+
+case class RDFGenerationError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class RMLGenerationError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class ShapesGenerationError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class JDBCDriverError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class QueryError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class VarError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class FileError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class FunctionExecutionError(message: String, parserInfo: ParserInfo = UnknownParserInfo) extends ShExMLError
+case class SemanticCheckerError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class LexerError(message: String, parserInfo: ParserInfo) extends ShExMLError
+case class ParserError(message: String, parserInfo: ParserInfo) extends ShExMLError
+
+class LexerErrorListener extends BaseErrorListener {
+ override def syntaxError(recognizer: Recognizer[_, _], offendingSymbol: Any, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException): Unit = {
+ throw LexerError(s"Lexer error: $msg",
+ new ParserInfo(Some(line), Some(charPositionInLine), Some(line), Some(charPositionInLine + 1)))
+ }
+}
+
+class ParserErrorListener extends BaseErrorListener {
+ override def syntaxError(recognizer: Recognizer[_, _], offendingSymbol: Any, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException): Unit = {
+ val endColumn = offendingSymbol match {
+ case ct: CommonToken => Some(charPositionInLine + (ct.getStopIndex - ct.getStartIndex) + 1)
+ case _ => Some(charPositionInLine + 1)
+ }
+ throw ParserError(s"Parser error: $msg",
+ new ParserInfo(Some(line), Some(charPositionInLine), Some(line), endColumn))
+ }
+}
diff --git a/src/main/scala/com/herminiogarcia/shexml/parser/ASTCreatorVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/parser/ASTCreatorVisitor.scala
index 16aa147..63b3e1b 100644
--- a/src/main/scala/com/herminiogarcia/shexml/parser/ASTCreatorVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/parser/ASTCreatorVisitor.scala
@@ -4,10 +4,10 @@ import com.herminiogarcia.shexml.antlr.ShExMLParser._
import com.herminiogarcia.shexml.antlr.ShExMLParserBaseVisitor
import com.herminiogarcia.shexml.ast._
import com.herminiogarcia.shexml.ast.URL
+import org.antlr.v4.runtime.ParserRuleContext
import scala.collection.JavaConverters._
import scala.util.Try
-
/**
* Created by herminio on 20/12/17.
*/
@@ -17,67 +17,67 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val declarations = ctx.decl().asScala.map(visit(_).asInstanceOf[Declaration]).toList
val graphs = ctx.graph().asScala.map(visit(_).asInstanceOf[Graph]).toList
val shapes = ctx.shape().asScala.map(visit(_).asInstanceOf[Shape]).toList
- ShExML(declarations, graphs, shapes)
+ ShExML(declarations, graphs, shapes, createParserInfo(ctx))
}
override def visitDecl(ctx: DeclContext): AST = {
val declarationStatement = super.visitDecl(ctx).asInstanceOf[DeclarationStatement]
- Declaration(declarationStatement)
+ Declaration(declarationStatement, createParserInfo(ctx))
}
override def visitPrefix(ctx: PrefixContext): AST = {
- val url = URL(ctx.URL().getText)
+ val url = URL(ctx.URL().getText, createParserInfo(ctx))
val name = createVar(ctx.variable())
- Prefix(name, url)
+ Prefix(name, url, createParserInfo(ctx))
}
override def visitSource(ctx: SourceContext): AST = {
val url =
- if(ctx.URL != null) URL(ctx.URL().getText)
- else if(ctx.STDIN() != null) Stdin()
- else if(ctx.QUERY_PART() != null) RelativePath(ctx.QUERY_PART().getText)
- else JdbcURL(ctx.JDBC_URL().getText)
+ if(ctx.URL != null) URL(ctx.URL().getText, createParserInfo(ctx))
+ else if(ctx.STDIN() != null) Stdin(createParserInfo(ctx))
+ else if(ctx.QUERY_PART() != null) RelativePath(ctx.QUERY_PART().getText, createParserInfo(ctx))
+ else JdbcURL(ctx.JDBC_URL().getText, createParserInfo(ctx))
val name = createVar(ctx.variable())
- Source(name, url)
+ Source(name, url, createParserInfo(ctx))
}
override def visitFunctions(ctx: FunctionsContext): AST = {
val url =
- if(ctx.URL != null) URL(ctx.URL().getText)
- else RelativePath(ctx.QUERY_PART().getText)
+ if(ctx.URL != null) URL(ctx.URL().getText, createParserInfo(ctx))
+ else RelativePath(ctx.QUERY_PART().getText, createParserInfo(ctx))
val name = createVar(ctx.variable())
- Functions(name, url)
+ Functions(name, url, createParserInfo(ctx))
}
override def visitQuery(ctx: QueryContext): AST = {
val queryOrURL: QueryOrURL = if (ctx.URL() != null) {
- URL(ctx.URL().getText)
+ URL(ctx.URL().getText, createParserInfo(ctx))
} else {
visitQueryClause(ctx.queryClause()).asInstanceOf[QueryClause]
}
val name = createVar(ctx.variable())
- Query(name, queryOrURL)
+ Query(name, queryOrURL, createParserInfo(ctx))
}
override def visitQueryClause(ctx: QueryClauseContext): AST = {
- if(ctx.JSONPATH() != null) JsonPath(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "))
- else if(ctx.XMLPATH() != null) XmlPath(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "))
- else if(ctx.SQL() != null) SqlQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "))
+ if(ctx.JSONPATH() != null) JsonPath(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), createParserInfo(ctx))
+ else if(ctx.XMLPATH() != null) XmlPath(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), createParserInfo(ctx))
+ else if(ctx.SQL() != null) SqlQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), createParserInfo(ctx))
else if(ctx.SPARQL() != null) SparqlQuery(ctx.QUERY_PART().asScala.map(_.getText)
- .mkString(" ").replaceAll("\\\\<", "<").replaceAll("\\\\>", ">"))
- else CSVPerRow("")
+ .mkString(" ").replaceAll("\\\\<", "<").replaceAll("\\\\>", ">"), createParserInfo(ctx))
+ else CSVPerRow("", createParserInfo(ctx))
}
override def visitExpression(ctx: ExpressionContext): AST = {
val exp = visit(ctx.exp()).asInstanceOf[Exp]
val name = createVar(ctx.variable())
- Expression(name, exp)
+ Expression(name, exp, createParserInfo(ctx))
}
override def visitMatcher(ctx: MatcherContext): AST = {
val matcherVar = createVar(ctx.variable())
val matchers = visit(ctx.matchers()).asInstanceOf[MatcherList]
- Matchers(matcherVar, matchers)
+ Matchers(matcherVar, matchers, createParserInfo(ctx))
}
override def visitMatchers(ctx: MatchersContext): AST = {
@@ -90,7 +90,7 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val replacementString =
if(ctx.STRING_OR_VAR() != null ) ctx.STRING_OR_VAR().getText
else ctx.STRINGOPERATOR().getText.replaceAll("\"", "")
- MatcherList(otherMatchers.::(Matcher(replacedStrings, replacementString)))
+ MatcherList(otherMatchers.::(Matcher(replacedStrings, replacementString, createParserInfo(ctx))), createParserInfo(ctx))
}
override def visitReplacedStrings(ctx: ReplacedStringsContext): AST = {
@@ -102,7 +102,7 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val string =
if(ctx.STRING_OR_VAR() != null ) ctx.STRING_OR_VAR().getText
else ctx.STRINGOPERATOR().getText.replaceAll("\"", "")
- ReplacedStrings(replacedStrings.::(string))
+ ReplacedStrings(replacedStrings.::(string), createParserInfo(ctx))
}
override def visitAutoincrement(ctx: AutoincrementContext): AST = {
@@ -124,27 +124,27 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
else if(ctx.STRINGOPERATOR_AUTOINCREMENT(0) != null && ctx.STRINGOPERATOR_AUTOINCREMENT(1) != null)
Some(ctx.STRINGOPERATOR_AUTOINCREMENT(1).getText.replaceAll("\"", ""))
else None
- AutoIncrement(name, from, to, by, precedentString, closingString)
+ AutoIncrement(name, from, to, by, precedentString, closingString, createParserInfo(ctx))
}
override def visitUnion(ctx: UnionContext): AST = {
val left = visit(ctx.leftUnionOption()).asInstanceOf[LeftUnion]
val right = visit(ctx.rightUnionOption()).asInstanceOf[RightUnion]
- Union(left, right)
+ Union(left, right, createParserInfo(ctx))
}
override def visitStringOperation(ctx: StringOperationContext): AST = {
val left = visit(ctx.iteratorQuery(0)).asInstanceOf[IteratorQuery]
val right = visit(ctx.iteratorQuery(1)).asInstanceOf[IteratorQuery]
val stringOperator = ctx.STRINGOPERATOR().getText.replace("\"", "")
- StringOperation(left, right, stringOperator)
+ StringOperation(left, right, stringOperator, createParserInfo(ctx))
}
override def visitSubstitution(ctx: SubstitutionContext): AST = {
val leftUnion = visit(ctx.iteratorQuery(0)).asInstanceOf[IteratorQuery]
val rightUnion = visit(ctx.iteratorQuery(1)).asInstanceOf[IteratorQuery]
val joinClause = visit(ctx.iteratorQuery(2)).asInstanceOf[IteratorQuery]
- Substitution(leftUnion, rightUnion, joinClause)
+ Substitution(leftUnion, rightUnion, joinClause, createParserInfo(ctx))
}
override def visitJoin(ctx: JoinContext): AST = {
@@ -152,35 +152,35 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val rightUnion = visit(ctx.iteratorQuery(1)).asInstanceOf[IteratorQuery]
val leftJoinClause = visit(ctx.iteratorQuery(2)).asInstanceOf[IteratorQuery]
val rightJoinClause = visit(ctx.iteratorQuery(3)).asInstanceOf[IteratorQuery]
- Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause)
+ Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause, createParserInfo(ctx))
}
override def visitIterator(ctx: IteratorContext): AST = {
val query: QueryOrVar = if(ctx.queryClause() != null) {
visit(ctx.queryClause()).asInstanceOf[QueryClause]
} else {
- Var(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "))
+ Var(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), createParserInfo(ctx))
}
val variable = createVar(ctx.variable())
val fields = ctx.field().listIterator().asScala.map(visit(_).asInstanceOf[Field])
val iterators = ctx.nestedIterator().listIterator().asScala.map(visit(_).asInstanceOf[NestedIterator])
- Iterator(variable, query, fields.toList, iterators.toList)
+ Iterator(variable, query, fields.toList, iterators.toList, createParserInfo(ctx))
}
override def visitNestedIterator(ctx: NestedIteratorContext): AST = {
- val query = FieldQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "))
+ val query = FieldQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), parserInfo = createParserInfo(ctx))
val variable = createVar(ctx.variable())
val fields = ctx.field().listIterator().asScala.map(visit(_).asInstanceOf[Field])
val iterators = ctx.nestedIterator().listIterator().asScala.map(visit(_).asInstanceOf[NestedIterator])
- NestedIterator(variable, query, fields.toList, iterators.toList)
+ NestedIterator(variable, query, fields.toList, iterators.toList, createParserInfo(ctx))
}
override def visitField(ctx: FieldContext): AST = {
val pushed = ctx.PUSHED_FIELD() != null
val popped = ctx.POPPED_FIELD() != null
- val fieldQuery = FieldQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), pushed, popped)
+ val fieldQuery = FieldQuery(ctx.QUERY_PART().asScala.map(_.getText).mkString(" "), pushed, popped, createParserInfo(ctx))
val variable = createVar(ctx.variable())
- Field(variable, fieldQuery, pushed, popped)
+ Field(variable, fieldQuery, pushed, popped, createParserInfo(ctx))
}
override def visitIteratorQuery(ctx: IteratorQueryContext): AST = {
@@ -189,7 +189,7 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val builtinFunction =
if(ctx.builtinFunction() != null) Some(visit(ctx.builtinFunction()).asInstanceOf[BuiltinFunction])
else None
- IteratorQuery(firstVar, varOrIteratorQuery, builtinFunction)
+ IteratorQuery(firstVar, varOrIteratorQuery, builtinFunction, createParserInfo(ctx))
}
override def visitComposedVariable(ctx: ComposedVariableContext): AST = {
@@ -198,44 +198,44 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val otherVariables = if(ctx.composedVariable() != null)
visit(ctx.composedVariable()).asInstanceOf[VarOrIteratorQuery]
else null
- if(otherVariables != null) IteratorQuery(variable, otherVariables) else variable
+ if(otherVariables != null) IteratorQuery(variable, otherVariables, parserInfo = createParserInfo(ctx, considerFullContext = true)) else variable
}
override def visitBuiltinFunction(ctx: BuiltinFunctionContext): AST = {
if(ctx.INDEX() != null) {
- Index()
+ Index(createParserInfo(ctx))
} else null
}
override def visitGraph(ctx: GraphContext): AST = {
val graphName = createGraphVar(ctx.literalValue)
val shapes = ctx.shape().asScala.map(visit(_).asInstanceOf[Shape]).toList
- val dummyGraph = Graph(graphName, Nil)
+ val dummyGraph = Graph(graphName, Nil, UnknownParserInfo)
val graphShapes = shapes.map(s =>
- Shape(s.shapeName, s.action, s.predicateObjects, Some(dummyGraph)))
- Graph(graphName, graphShapes)
+ Shape(s.shapeName, s.action, s.predicateObjects, Some(dummyGraph), s.parserInfo))
+ Graph(graphName, graphShapes, createParserInfo(ctx))
}
override def visitShape(ctx: ShapeContext): AST = {
val shapeName = createShapeVar(ctx.tripleElement)
val action = visit(ctx.actionOrLiteral()).asInstanceOf[ActionOrLiteral]
val predicateObjects = ctx.predicateObject().asScala.map(visit(_).asInstanceOf[PredicateObject]).toList
- Shape(shapeName, action, predicateObjects, None)
+ Shape(shapeName, action, predicateObjects, None, createParserInfo(ctx))
}
override def visitAction(ctx: ActionContext): AST = {
- val shapePrefix = ctx.prefixVar().getText
+ val shapePrefix = createVar(ctx.prefixVar().variable())
val action = visit(ctx.expOrVarOrFunctionCallign(0)).asInstanceOf[ExpOrVar]
val condition = if(ctx.expOrVarOrFunctionCallign(1) != null)
Some(visit(ctx.expOrVarOrFunctionCallign(1)).asInstanceOf[ExpOrVar])
else None
- Action(shapePrefix, action, condition)
+ Action(shapePrefix, action, condition, createParserInfo(ctx))
}
override def visitLiteralSubject(ctx: LiteralSubjectContext): AST = {
- val prefix = Var(ctx.prefixVar().getText)
+ val prefix = Var(ctx.prefixVar().getText, createParserInfo(ctx))
val value = ctx.variable().getText
- LiteralSubject(prefix, value)
+ LiteralSubject(prefix, value, createParserInfo(ctx))
}
override def visitPredicateObject(ctx: PredicateObjectContext): AST = {
@@ -245,32 +245,35 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
else if(ctx.shapeLink() == null && ctx.literalValue() == null)
visit(ctx.objectElement()).asInstanceOf[ObjectElement]
else visit(ctx.literalValue()).asInstanceOf[LiteralObject]
- PredicateObject(predicate, objectElementOrShapeLink)
+ PredicateObject(predicate, objectElementOrShapeLink, createParserInfo(ctx))
}
override def visitPredicate(ctx: PredicateContext): AST = {
if(ctx.A() != null) {
- Predicate("rdf:", "type")
+ Predicate("rdf:", "type", createParserInfo(ctx))
} else {
val prefix = ctx.literalValue().prefixVar().getText
val name = ctx.literalValue().variable().getText.replaceAll("\\\\.|%2E", ".")
- Predicate(prefix, name)
+ Predicate(prefix, name, createParserInfo(ctx))
}
}
override def visitLiteralValue(ctx: LiteralValueContext): AST = {
- val prefix = Var(ctx.prefixVar().getText)
+ val prefix = Var(ctx.prefixVar().getText, createParserInfo(ctx))
val value = ctx.variable().getText
- LiteralObject(prefix, value)
+ LiteralObject(prefix, value, createParserInfo(ctx))
}
override def visitObjectElement(ctx: ObjectElementContext): AST = {
- val prefix = if(ctx.firstPartObjectElement().prefixVar() != null) ctx.firstPartObjectElement().prefixVar().getText else ""
+ val prefix =
+ if(ctx.firstPartObjectElement().prefixVar() != null)
+ Some(Var(ctx.firstPartObjectElement().prefixVar().getText, createParserInfo(ctx.firstPartObjectElement().prefixVar(), considerFullContext = true)))
+ else None
val mainExpOrVar = if(ctx.firstPartObjectElement().valueRetriever().expOrVarOrFunctionCallign(0) != null)
Some(visit(ctx.firstPartObjectElement().valueRetriever().expOrVarOrFunctionCallign(0)).asInstanceOf[ExpOrVar])
else None
val literalValue = if(ctx.firstPartObjectElement().valueRetriever().STRINGOPERATOR() != null)
- Some(LiteralObjectValue(ctx.firstPartObjectElement().valueRetriever().STRINGOPERATOR().getText.replaceAll("\"", "")))
+ Some(LiteralObjectValue(ctx.firstPartObjectElement().valueRetriever().STRINGOPERATOR().getText.replaceAll("\"", ""), createParserInfo(ctx)))
else None
val matcherVar = if(ctx.firstPartObjectElement().valueRetriever().variable() != null)
Option(ctx.firstPartObjectElement().valueRetriever().variable()).map(createVar)
@@ -283,13 +286,13 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val rdfCollection = {
if(ctx.firstPartObjectElement() == null || ctx.firstPartObjectElement().valueRetriever() == null
|| ctx.firstPartObjectElement().valueRetriever().rdfCollection() == null) None
- else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFLIST() != null) Some(RDFList())
- else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFSEQ() != null) Some(RDFSeq())
- else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFALT() != null) Some(RDFAlt())
- else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFBAG() != null) Some(RDFBag())
+ else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFLIST() != null) Some(RDFList(createParserInfo(ctx)))
+ else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFSEQ() != null) Some(RDFSeq(createParserInfo(ctx)))
+ else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFALT() != null) Some(RDFAlt(createParserInfo(ctx)))
+ else if(ctx.firstPartObjectElement().valueRetriever().rdfCollection().RDFBAG() != null) Some(RDFBag(createParserInfo(ctx)))
else None
}
- ObjectElement(prefix, mainExpOrVar, literalValue, matcherVar, condition, dataType, langTag, rdfCollection)
+ ObjectElement(prefix, mainExpOrVar, literalValue, matcherVar, condition, dataType, langTag, rdfCollection, createParserInfo(ctx))
}
override def visitExpOrVarOrFunctionCallign(ctx: ExpOrVarOrFunctionCallignContext): AST = {
@@ -300,26 +303,26 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
override def visitXmlschemadatatype(ctx: XmlschemadatatypeContext): AST = {
if(ctx.firstPartObjectElement() == null) {
- DataTypeLiteral(ctx.XMLSCHEMADATATYPE().getText)
+ DataTypeLiteral(ctx.XMLSCHEMADATATYPE().getText, createParserInfo(ctx))
} else {
- val prefix = if(ctx.firstPartObjectElement().prefixVar() != null) ctx.firstPartObjectElement().prefixVar().getText else ""
+ val prefix = if(ctx.firstPartObjectElement().prefixVar() != null) Some(createVar(ctx.firstPartObjectElement().prefixVar().variable())) else None
val expOrVar = visit(ctx.firstPartObjectElement().valueRetriever().expOrVarOrFunctionCallign(0)).asInstanceOf[ExpOrVar]
val matcherVar = if(ctx.firstPartObjectElement().valueRetriever().variable() != null)
Option(ctx.firstPartObjectElement().valueRetriever().variable()).map(createVar)
else None
- DataTypeGeneration(prefix, expOrVar, matcherVar)
+ DataTypeGeneration(prefix, expOrVar, matcherVar, createParserInfo(ctx))
}
}
override def visitLangtag(ctx: LangtagContext): AST = {
if(ctx.valueRetriever() == null) {
- LangTagLiteral(ctx.LANGTAG().getText.replace("@", ""))
+ LangTagLiteral(ctx.LANGTAG().getText.replace("@", ""), createParserInfo(ctx))
} else {
val expOrVar = visit(ctx.valueRetriever().expOrVarOrFunctionCallign(0)).asInstanceOf[ExpOrVar]
val matcherVar = if(ctx.valueRetriever().variable() != null)
Option(ctx.valueRetriever().variable()).map(createVar)
else None
- LangTagGeneration(expOrVar, matcherVar)
+ LangTagGeneration(expOrVar, matcherVar, createParserInfo(ctx))
}
}
@@ -327,7 +330,7 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val functionHub = createVar(ctx.variable(0))
val functionName = createVar(ctx.variable(1))
val functionArguments = visit(ctx.functionArguments()).asInstanceOf[Arguments]
- FunctionCalling(functionHub, functionName, functionArguments)
+ FunctionCalling(functionHub, functionName, functionArguments, createParserInfo(ctx))
}
override def visitFunctionArguments(ctx: FunctionArgumentsContext): AST = {
@@ -337,33 +340,41 @@ class ASTCreatorVisitor extends ShExMLParserBaseVisitor[AST] {
val arguments =
if(ctx.functionArguments() != null) argument +: visit(ctx.functionArguments()).asInstanceOf[Arguments].arguments
else List(argument)
- Arguments(arguments)
+ Arguments(arguments, createParserInfo(ctx))
}
override def visitShapeLink(ctx: ShapeLinkContext): AST = {
- val shapeName = ShapeVar(ctx.getText.replace("@", "").replaceAll("\\\\.|%2E", "."))
- ShapeLink(shapeName)
+ val shapeName = ShapeVar(ctx.getText.replace("@", "").replaceAll("\\\\.|%2E", "."), createParserInfo(ctx))
+ ShapeLink(shapeName, createParserInfo(ctx, considerFullContext = true))
}
def createVar(variable: VariableContext): Var = {
- Var(Try(variable.getText).getOrElse("").replaceAll("\\\\.|%2E", "."))
+ Var(Try(variable.getText).getOrElse("").replaceAll("\\\\.|%2E", "."), createParserInfo(variable, considerFullContext = true))
}
def createShapeVar(tripleElementContext: TripleElementContext): ShapeVar = {
- ShapeVar(tripleElementContext.getText.replaceAll("\\\\.|%2E", "."))
+ ShapeVar(tripleElementContext.getText.replaceAll("\\\\.|%2E", "."), createParserInfo(tripleElementContext))
}
def createGraphVar(literalValueContext: LiteralValueContext): GraphVar = {
GraphVar(literalValueContext.prefixVar().getText,
- literalValueContext.variable().getText.replaceAll("\\\\.|%2E", "."))
+ literalValueContext.variable().getText.replaceAll("\\\\.|%2E", "."),
+ createParserInfo(literalValueContext))
}
def getDeclarationContent(content: String): String = content.replaceAll("<>", "")
def createQuery(query: String): QueryClause = {
- if(query.startsWith("$")) JsonPath(query)
- else if(query.startsWith("/")) XmlPath(query)
+ if(query.startsWith("$")) JsonPath(query, UnknownParserInfo)
+ else if(query.startsWith("/")) XmlPath(query, UnknownParserInfo)
else throw new Exception("Impossible to parse query: " + query)
}
+ def createParserInfo(context: ParserRuleContext, considerFullContext: Boolean = false): ParserInfo = {
+ val start = context.getStart
+ val end = context.getStop
+ val endPosition = if(considerFullContext) Some(end.getCharPositionInLine + end.getText.length) else Some(end.getCharPositionInLine)
+ new ParserInfo(Some(start.getLine), Some(start.getCharPositionInLine), Some(end.getLine), endPosition)
+ }
+
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/shex/ShExGeneratorVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/shex/ShExGeneratorVisitor.scala
index 5fde7bc..7caeaf2 100644
--- a/src/main/scala/com/herminiogarcia/shexml/shex/ShExGeneratorVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/shex/ShExGeneratorVisitor.scala
@@ -1,6 +1,7 @@
package com.herminiogarcia.shexml.shex
import com.herminiogarcia.shexml.ast._
+import com.herminiogarcia.shexml.helper.ShapesGenerationError
import com.herminiogarcia.shexml.visitor.DefaultVisitor
import com.typesafe.scalalogging.Logger
@@ -15,8 +16,9 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
val prefixTable = mutable.HashMap[String, String]()
override def doVisit(ast: com.herminiogarcia.shexml.ast.AST, optionalArgument: HashMap[String, String]): ShExSubsetAST = ast match {
- case ShExML(declarations, graphs, shapes) => {
- val prefixes = (declarations :+ Declaration(com.herminiogarcia.shexml.ast.Prefix(Var("rdf:"), URL("http://www.w3.org/1999/02/22-rdf-syntax-ns#"))))
+ case ShExML(declarations, graphs, shapes, _) => {
+ val prefixes = (declarations :+ Declaration(com.herminiogarcia.shexml.ast.Prefix(Var("rdf:"),
+ URL("http://www.w3.org/1999/02/22-rdf-syntax-ns#", UnknownParserInfo), UnknownParserInfo), UnknownParserInfo))
.filter(_.declarationStatement.isInstanceOf[com.herminiogarcia.shexml.ast.Prefix])
.map(doVisit(_, optionalArgument).asInstanceOf[com.herminiogarcia.shexml.shex.Prefix])
val shexGraphs = graphs.map(doVisit(_, optionalArgument).asInstanceOf[com.herminiogarcia.shexml.shex.Graph])
@@ -27,22 +29,22 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
result
}
- case Declaration(declarationStatement) => {
+ case Declaration(declarationStatement, _) => {
doVisit(declarationStatement, optionalArgument)
}
- case com.herminiogarcia.shexml.ast.Prefix(name, url) => {
+ case com.herminiogarcia.shexml.ast.Prefix(name, url, _) => {
prefixTable += (name.name -> url.value)
com.herminiogarcia.shexml.shex.Prefix(name.name, url)
}
- case com.herminiogarcia.shexml.ast.Graph(graphName, shapes) => {
+ case com.herminiogarcia.shexml.ast.Graph(graphName, shapes, _) => {
logger.info(s"Extracting ShEx for ${shapes.size} shapes within the graph ${graphName.prefix + graphName.name}")
val shexShapes = shapes.map(doVisit(_, optionalArgument).asInstanceOf[com.herminiogarcia.shexml.shex.Shape])
com.herminiogarcia.shexml.shex.Graph(graphName.prefix + graphName.name, shexShapes)
}
- case com.herminiogarcia.shexml.ast.Shape(shapeName, action, predicateObjects, _) => {
+ case com.herminiogarcia.shexml.ast.Shape(shapeName, action, predicateObjects, _, _) => {
logger.info(s"Extracting shape ${shapeName.name} with ${predicateObjects.size} predicate-object statements")
val shapePrefix = getShapePrefix(action)
val arguments = HashMap("shapeName" -> shapeName.name)
@@ -52,7 +54,7 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
com.herminiogarcia.shexml.shex.Shape(name, iriStart, shexPredicateObjects)
}
- case com.herminiogarcia.shexml.ast.PredicateObject(predicate, objectOrShapeLink) => {
+ case com.herminiogarcia.shexml.ast.PredicateObject(predicate, objectOrShapeLink, _) => {
val fullPrefix = prefixTable.getOrElse(predicate.prefix, "")
val arguments = optionalArgument + ("predicateIRI" -> (fullPrefix + predicate.`extension`))
val shexPredicate = doVisit(predicate, optionalArgument).asInstanceOf[com.herminiogarcia.shexml.shex.Predicate]
@@ -60,11 +62,11 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
com.herminiogarcia.shexml.shex.PredicateObject(shexPredicate, objectElement)
}
- case com.herminiogarcia.shexml.ast.Predicate(prefix, localname) => {
+ case com.herminiogarcia.shexml.ast.Predicate(prefix, localname, _) => {
com.herminiogarcia.shexml.shex.Predicate(prefix, localname)
}
- case com.herminiogarcia.shexml.ast.ObjectElement(prefix, _, literalValue, _, _, dataType, langTag, _) => {
+ case com.herminiogarcia.shexml.ast.ObjectElement(prefix, _, literalValue, _, _, dataType, langTag, _, _) => {
val shapeName = optionalArgument("shapeName")
val predicateIRI = optionalArgument("predicateIRI")
val cardinality = getInferredCardinality(shapeName, predicateIRI)
@@ -84,25 +86,25 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
case None =>
if(prefix.isEmpty) {
if(langTag.isDefined) langTag.get match {
- case LangTagLiteral(value) => FixedValue("@" + value)
- case LangTagGeneration(action, matcher) => throw new Exception("ShEx generation with dynamic langtag is not yet supported!")
+ case LangTagLiteral(value, _) => FixedValue("@" + value)
+ case LangTagGeneration(action, matcher, parserInfo) => throw ShapesGenerationError("ShEx generation with dynamic langtag is not yet supported!", parserInfo)
}
else
ObjectDefinition(shexDatatype, cardinality)
}
- else PartialFixedValue(prefix, cardinality)
+ else PartialFixedValue(prefix.get.name, cardinality)
}
}
- case com.herminiogarcia.shexml.ast.ShapeLink(shapeVar) => {
+ case com.herminiogarcia.shexml.ast.ShapeLink(shapeVar, _) => {
com.herminiogarcia.shexml.shex.ShapeLink(shapeVar.name)
}
- case com.herminiogarcia.shexml.ast.LiteralObject(prefix, value) => {
+ case com.herminiogarcia.shexml.ast.LiteralObject(prefix, value, _) => {
FixedValue(prefix.name + value)
}
- case com.herminiogarcia.shexml.ast.LiteralObjectValue(value) => {
+ case com.herminiogarcia.shexml.ast.LiteralObjectValue(value, _) => {
FixedValue('"' + value + '"')
}
@@ -124,8 +126,8 @@ class ShExGeneratorVisitor(inferences: List[ShExMLInferredCardinalitiesAndDataty
}
protected def getShapePrefix(action: ActionOrLiteral): String = action match {
- case Action(shapePrefix, _, _) => shapePrefix
- case LiteralSubject(prefix, _) => prefix.name
+ case Action(shapePrefix, _, _, _) => shapePrefix.name
+ case LiteralSubject(prefix, _, _) => prefix.name
}
override def doVisitDefault(): ShExSubsetAST = ???
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultUnitVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultUnitVisitor.scala
index 9ba9015..d7984f9 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultUnitVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultUnitVisitor.scala
@@ -9,55 +9,55 @@ abstract class DefaultUnitVisitor {
def visit(ast: AST): Unit = ast match {
- case ShExML(declarations, graphs, shapes) => {
+ case ShExML(declarations, graphs, shapes, _) => {
declarations.foreach(doVisit)
graphs.foreach(doVisit)
shapes.foreach(doVisit)
}
- case Declaration(declarationStatement) => doVisit(declarationStatement)
+ case Declaration(declarationStatement, _) => doVisit(declarationStatement)
- case Graph(_, shapes) => shapes.foreach(doVisit)
+ case Graph(_, shapes, _) => shapes.foreach(doVisit)
- case Shape(shapeName, action, predicateObjects, _) => {
+ case Shape(shapeName, action, predicateObjects, _, _) => {
doVisit(shapeName)
doVisit(action)
predicateObjects.foreach(doVisit)
}
- case Query(_, queryClause) => doVisit(queryClause)
+ case Query(_, queryClause, _) => doVisit(queryClause)
- case Expression(_, exp) => doVisit(exp)
+ case Expression(_, exp, _) => doVisit(exp)
- case Union(left, right) => {
+ case Union(left, right, _) => {
doVisit(left)
doVisit(right)
}
- case Substitution(leftUnion, rightUnion, joinClause) => {
+ case Substitution(leftUnion, rightUnion, joinClause, _) => {
doVisit(leftUnion)
doVisit(rightUnion)
doVisit(joinClause)
}
- case Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause) => {
+ case Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause, _) => {
doVisit(leftUnion)
doVisit(rightUnion)
doVisit(leftJoinClause)
doVisit(rightJoinClause)
}
- case StringOperation(left, right, _) => {
+ case StringOperation(left, right, _, _) => {
doVisit(left)
doVisit(right)
}
- case PredicateObject(objectOrShapeLink, predicate) => {
+ case PredicateObject(objectOrShapeLink, predicate, _) => {
doVisit(objectOrShapeLink)
doVisit(predicate)
}
- case ObjectElement(_, action, _, _, _, _, _, _) => action match { case Some(value) => doVisit(value) }
+ case ObjectElement(_, action, _, _, _, _, _, _, _) => action match { case Some(value) => doVisit(value) }
case _ => // do nothing
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultVisitor.scala
index 0541c3d..31ed172 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/DefaultVisitor.scala
@@ -9,54 +9,54 @@ abstract class DefaultVisitor[A, B] {
def visit(ast: AST, optionalArgument: A): B = ast match {
- case ShExML(declarations, graphs, shapes) => {
+ case ShExML(declarations, graphs, shapes, _) => {
declarations.foreach(doVisit(_, optionalArgument))
val firstGraph = graphs.map(doVisit(_, optionalArgument)).headOption
val firstShape = shapes.map(doVisit(_, optionalArgument)).headOption
if(firstGraph.isEmpty) firstShape.get else firstGraph.get
}
- case Declaration(declarationStatement) => doVisit(declarationStatement, optionalArgument)
+ case Declaration(declarationStatement, _) => doVisit(declarationStatement, optionalArgument)
- case Shape(shapeName, action, predicateObjects, _) => {
+ case Shape(shapeName, action, predicateObjects, _, _) => {
doVisit(shapeName, optionalArgument)
predicateObjects.foreach(doVisit(_, optionalArgument))
doVisit(action, optionalArgument)
}
- case Query(_, queryClause) => doVisit(queryClause, optionalArgument)
+ case Query(_, queryClause, _) => doVisit(queryClause, optionalArgument)
- case Expression(_, exp) => doVisit(exp, optionalArgument)
+ case Expression(_, exp, _) => doVisit(exp, optionalArgument)
- case Union(left, right) => {
+ case Union(left, right, _) => {
doVisit(left, optionalArgument)
doVisit(right, optionalArgument)
}
- case Substitution(leftUnion, rightUnion, joinClause) => {
+ case Substitution(leftUnion, rightUnion, joinClause, _) => {
doVisit(leftUnion, optionalArgument)
doVisit(rightUnion, optionalArgument)
doVisit(joinClause, optionalArgument)
}
- case Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause) => {
+ case Join(leftUnion, rightUnion, leftJoinClause, rightJoinClause, _) => {
doVisit(leftUnion, optionalArgument)
doVisit(rightUnion, optionalArgument)
doVisit(leftJoinClause, optionalArgument)
doVisit(rightJoinClause, optionalArgument)
}
- case StringOperation(left, right, _) => {
+ case StringOperation(left, right, _, _) => {
doVisit(left, optionalArgument)
doVisit(right, optionalArgument)
}
- case PredicateObject(objectOrShapeLink, predicate) => {
+ case PredicateObject(objectOrShapeLink, predicate, _) => {
doVisit(objectOrShapeLink, optionalArgument)
doVisit(predicate, optionalArgument)
}
- case ObjectElement(_, action, _, _, _, _, _, _) => action match { case Some(value) => doVisit(value, optionalArgument) }
+ case ObjectElement(_, action, _, _, _, _, _, _, _) => action match { case Some(value) => doVisit(value, optionalArgument) }
case _ => doVisitDefault()
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/JdbcDriverRegistry.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/JdbcDriverRegistry.scala
index f50e9d9..247e3ba 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/JdbcDriverRegistry.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/JdbcDriverRegistry.scala
@@ -1,5 +1,8 @@
package com.herminiogarcia.shexml.visitor
+import com.herminiogarcia.shexml.ast.ParserInfo
+import com.herminiogarcia.shexml.helper.JDBCDriverError
+
trait JdbcDriverRegistry {
val jdbcDrivers = Map(
@@ -14,10 +17,10 @@ trait JdbcDriverRegistry {
"jdbc:h2" -> "org.h2.Driver"
)
- def lookForJdbcDriver(jdbcURL: String, moreDrivers: Map[String, String] = Map()): String = {
+ def lookForJdbcDriver(jdbcURL: String, moreDrivers: Map[String, String] = Map(), parserInfo: ParserInfo): String = {
(jdbcDrivers ++ moreDrivers).find(t => jdbcURL.startsWith(t._1)) match {
case Some(driver) => driver._2
- case None => throw new Exception("No driver found for URL: " + jdbcURL)
+ case None => throw JDBCDriverError("No driver found for URL: " + jdbcURL, parserInfo)
}
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/PushedOrPoppedValueSearchVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/PushedOrPoppedValueSearchVisitor.scala
index 02808e2..0303e7a 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/PushedOrPoppedValueSearchVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/PushedOrPoppedValueSearchVisitor.scala
@@ -6,23 +6,23 @@ class PushedOrPoppedValueSearchVisitor extends DefaultVisitor[Any, Boolean] {
override def doVisit(ast: AST, optionalArgument: Any): Boolean = ast match {
- case ShExML(declarations, graphs, shapes) => {
+ case ShExML(declarations, graphs, shapes, _) => {
declarations.map(doVisit(_, optionalArgument)).find(_.self).getOrElse(false)
}
- case Iterator(_, _, fields, iterators) => {
+ case Iterator(_, _, fields, iterators, _) => {
val resultInFields = fields.map(doVisit(_, optionalArgument)).find(_.self).getOrElse(false)
val resultsInIterators = iterators.map(doVisit(_, optionalArgument)).find(_.self).getOrElse(false)
resultInFields || resultsInIterators
}
- case NestedIterator(_, _, fields, iterators) => {
+ case NestedIterator(_, _, fields, iterators, _) => {
val resultInFields = fields.map(doVisit(_, optionalArgument)).find(_.self).getOrElse(false)
val resultsInIterators = iterators.map(doVisit(_, optionalArgument)).find(_.self).getOrElse(false)
resultInFields || resultsInIterators
}
- case Field(_, _, pushed, popped) => pushed || popped
+ case Field(_, _, pushed, popped, _) => pushed || popped
case default => visit(default, optionalArgument)
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/QuerySearcher.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/QuerySearcher.scala
index d05ef76..8e82f89 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/QuerySearcher.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/QuerySearcher.scala
@@ -1,7 +1,7 @@
package com.herminiogarcia.shexml.visitor
import com.herminiogarcia.shexml.ast._
-import com.herminiogarcia.shexml.helper.SourceHelper
+import com.herminiogarcia.shexml.helper.{FileError, QueryError, SourceHelper, VarError}
class QuerySearcher(val varTable: Map[Variable, VarResult]) {
@@ -15,13 +15,13 @@ class QuerySearcher(val varTable: Map[Variable, VarResult]) {
case Some(result) => result match {
case qv: QueryClause => qv
case u: URL => getURLContents(u)
- case _ => throw new Exception("Result of variable " + v.name + " not suitable as a query")
+ case _ => throw QueryError("Result of variable " + v.name + " not suitable as a query", variable.parserInfo)
}
- case None => throw new Exception("Variable " + v.name + " not defined")
+ case None => throw VarError("Variable " + v.name + " not defined", variable.parserInfo)
}
}
- case None => throw new Exception("Variable " + variable.name + " not found")
+ case None => throw VarError("Variable " + variable.name + " not found", variable.parserInfo)
}
}
@@ -30,14 +30,14 @@ class QuerySearcher(val varTable: Map[Variable, VarResult]) {
val extension = parts(parts.length - 1)
val file = new SourceHelper().getURLContent(u.value)
if(extension == "xpath") {
- XmlPath(file.fileContent)
+ XmlPath(file.fileContent, u.parserInfo)
} else if(extension == "jsonpath") {
- JsonPath(file.fileContent)
+ JsonPath(file.fileContent, u.parserInfo)
} else if(extension == "sql") {
- SqlQuery(file.fileContent)
+ SqlQuery(file.fileContent, u.parserInfo)
} else if(extension == "sparql") {
- SparqlQuery(file.fileContent)
- } else throw new Exception("File extension " + extension + " is not supported for queries")
+ SparqlQuery(file.fileContent, u.parserInfo)
+ } else throw FileError("File extension " + extension + " is not supported for queries", u.parserInfo)
}
}
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/RDFGeneratorVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/RDFGeneratorVisitor.scala
index 72a0233..dfd80c3 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/RDFGeneratorVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/RDFGeneratorVisitor.scala
@@ -1,8 +1,8 @@
package com.herminiogarcia.shexml.visitor
import com.github.tototoshi.csv.{CSVReader, DefaultCSVFormat}
-import com.herminiogarcia.shexml.ast.{AST, Action, ActionOrLiteral, AutoIncrement, BuiltinFunction, CSVPerRow, DataTypeGeneration, DataTypeLiteral, Declaration, Exp, FieldQuery, FilePath, FilePathOrStdin, FunctionCalling, Graph, Index, IteratorQuery, JdbcURL, Join, JsonPath, LangTagGeneration, LangTagLiteral, LiteralObject, LiteralObjectValue, LiteralSubject, Matcher, Matchers, ObjectElement, Predicate, PredicateObject, Prefix, QueryClause, RDFAlt, RDFBag, RDFCollection, RDFList, RDFSeq, RelativePath, ShExML, Shape, ShapeLink, ShapeVar, Sparql, SparqlColumn, Sql, SqlColumn, Stdin, StringOperation, Substitution, URL, Union, Var, VarResult, Variable, XmlPath}
-import com.herminiogarcia.shexml.helper.{FunctionHubExecutor, LoadedSource, ParallelExecutionConfigurator, SourceHelper}
+import com.herminiogarcia.shexml.ast.{AST, Action, ActionOrLiteral, AutoIncrement, BuiltinFunction, CSVPerRow, DataTypeGeneration, DataTypeLiteral, Declaration, Exp, FieldQuery, FilePath, FilePathOrStdin, FunctionCalling, Graph, Index, IteratorQuery, JdbcURL, Join, JsonPath, LangTagGeneration, LangTagLiteral, LiteralObject, LiteralObjectValue, LiteralSubject, Matcher, Matchers, ObjectElement, ParserInfo, Predicate, PredicateObject, Prefix, QueryClause, RDFAlt, RDFBag, RDFCollection, RDFList, RDFSeq, RelativePath, ShExML, Shape, ShapeLink, ShapeVar, Sparql, SparqlColumn, Sql, SqlColumn, Stdin, StringOperation, Substitution, URL, Union, Var, VarResult, Variable, XmlPath}
+import com.herminiogarcia.shexml.helper.{FunctionHubExecutor, LoadedSource, ParallelExecutionConfigurator, RDFGenerationError, SourceHelper}
import com.herminiogarcia.shexml.shex.{Node, ShExMLInferredCardinalitiesAndDatatypes, ShapeMapInference, ShapeMapShape}
import com.herminiogarcia.shexml.visitor
import com.jayway.jsonpath.{Configuration, DocumentContext}
@@ -62,11 +62,11 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
override def doVisit(ast: AST, optionalArgument: Any): Any = ast match {
- case ShExML(declarations, graphs, shapes) => {
+ case ShExML(declarations, graphs, shapes, _) => {
declarations.foreach(doVisit(_, optionalArgument))
val firstGraph = graphs.map(doVisit(_, optionalArgument)).headOption
val linkedShapes = shapes.flatMap(_.predicateObjects.flatMap(_.objectOrShapeLink match {
- case ShapeLink(shapeVar) => List(shapeVar)
+ case ShapeLink(shapeVar, _) => List(shapeVar)
case _ => List()
}))
val filteredShapes = shapes.filterNot(s => linkedShapes.contains(s.shapeName))
@@ -86,17 +86,17 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
if(firstShape.isEmpty) firstGraph.get
}
- case Declaration(declarationStatement) => {
+ case Declaration(declarationStatement, _) => {
if(declarationStatement.isInstanceOf[Prefix])
doVisit(declarationStatement, optionalArgument)
}
- case Prefix(variable, url) => {
+ case Prefix(variable, url, _) => {
prefixTable += ((variable.name, url.value))
defaultModel.setNsPrefix(variable.name.replace(":", ""), url.value)
}
- case Graph(graphName, shapes) => {
+ case Graph(graphName, shapes, _) => {
logger.info(s"Generating ${shapes.size} shapes within $graphName graph")
val shapesHead = shapes.headOption
val shapesTail = shapes.tail
@@ -109,7 +109,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
)
}
- case Shape(shapeName, action, predicateObjects, holdingGraph) => {
+ case Shape(shapeName, action, predicateObjects, holdingGraph, _) => {
logger.info(s"Generating shape ${shapeName.name} results with ${predicateObjects.size} predicate-object statements")
val shapePrefix = getShapePrefix(action)
val graphName = holdingGraph.map(g => prefixTable.getOrElse(g.graphName.prefix, "") + g.graphName.name).getOrElse("")
@@ -181,7 +181,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}), r.dataType, r.langTag, r.rdfCollection))
}
- case PredicateObject(predicate, objectOrShapeLink) => {
+ case PredicateObject(predicate, objectOrShapeLink, _) => {
val predicateResult = doVisit(predicate, optionalArgument)
val objectResult = doVisit(objectOrShapeLink, optionalArgument)
if(predicateResult != null && objectResult != null)
@@ -196,16 +196,16 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
else Nil
}
- case Predicate(prefix, extension) => {
+ case Predicate(prefix, extension, _) => {
prefixTable(prefix) + extension
}
- case ObjectElement(prefix, action, literalValue, matcher, condition, dataType, langTag, rdfCollection) => {
+ case ObjectElement(prefix, action, literalValue, matcher, condition, dataType, langTag, rdfCollection, parserInfo) => {
val result = action match {
case Some(value) => doVisit(value, optionalArgument)
case None => literalValue match {
case Some(literal) => doVisit(literal, optionalArgument)
- case None => throw new Exception("No generation clause given.")
+ case None => throw RDFGenerationError("No generation clause given.", parserInfo)
}
}
val matchedResultList = matcher match {
@@ -218,7 +218,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
result match {
case _: List[Result] =>
matchedResultList.asInstanceOf[List[Result]].map(result => {
- val newResults = result.results.map(prefixTable.getOrElse(prefix, "") + _)
+ val newResults = result.results.map(prefix.flatMap(p => prefixTable.get(p.name)).getOrElse("") + _)
val dataTypeValue = dataTypeResult.map({
case dataTypeResults: List[Result] => dataTypeResults.filter(_.id == result.id).head.results.head
case value: String => value
@@ -237,19 +237,19 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}).filter(_ != Nil)
case ResultAutoIncrement(iterator, predicate, _, _, _) =>
val dataTypeValue = dataTypeResult.map({
- case _: List[Result] => throw new Exception("Autoincrement values cannot have a generated dataType")
+ case _: List[Result] => throw RDFGenerationError("Autoincrement values cannot have a generated dataType", parserInfo)
case value: String => value
})
val langTagValue = langTagResult.map({
- case _: List[Result] => throw new Exception("Autoincrement values cannot have a generated langTag")
+ case _: List[Result] => throw RDFGenerationError("Autoincrement values cannot have a generated langTag", parserInfo)
case value: String => value
})
- visitor.ResultAutoIncrement(iterator, predicate, prefixTable.getOrElse(prefix, ""), normaliseDataType(dataTypeValue), langTagValue)
+ visitor.ResultAutoIncrement(iterator, predicate, prefix.flatMap(p => prefixTable.get(p.name)).getOrElse(""), normaliseDataType(dataTypeValue), langTagValue)
case _ => result
}
}
- case Union(left, right) => {
+ case Union(left, right, parserInfo) => {
val expName = optionalArgument.asInstanceOf[Map[String, Any]].getOrElse("varName", "")
val leftList = doVisit(left, optionalArgument)
val rightList = doVisit(right, optionalArgument)
@@ -262,13 +262,13 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
iteratorsCombinations += value
k -> leftResult.union(rightResult)
}).toMap
- case _ => throw new Exception("Impossible to combine an iterator with a non iterator expression")
+ case _ => throw RDFGenerationError("Impossible to combine an iterator with a non iterator expression", parserInfo)
}
case l: List[Result] => l.union(rightList.asInstanceOf[List[Result]])
}
}
- case Substitution(left, right, join) => {
+ case Substitution(left, right, join, parserInfo) => {
val expName = optionalArgument.asInstanceOf[Map[String, Any]].getOrElse("varName", "")
val leftList = doVisit(left, optionalArgument)
val rightList = doVisit(right, optionalArgument)
@@ -283,15 +283,15 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val value = s"$expName$k" -> getSubstitutionResults(leftResult, rightResult, joinResult)
iteratorsCombinations += value
})
- case _ => throw new Exception("Cannot join iterator with non iterator expression. Join clause is not an iterator expression")
+ case _ => throw RDFGenerationError("Cannot join iterator with non iterator expression. Join clause is not an iterator expression", parserInfo)
}
- case _ => throw new Exception("Cannot join iterator with non iterator expression. Right clause is not an iterator expression")
+ case _ => throw RDFGenerationError("Cannot join iterator with non iterator expression. Right clause is not an iterator expression", parserInfo)
}
case left: List[Result] => getSubstitutionResults(left, rightList.asInstanceOf[List[Result]], joinList.asInstanceOf[List[Result]])
}
}
- case Join(left, right, leftClause, rightClause) => {
+ case Join(left, right, leftClause, rightClause, parserInfo) => {
val expName = optionalArgument.asInstanceOf[Map[String, Any]].getOrElse("varName", "")
val leftList = doVisit(left, optionalArgument)
val rightList = doVisit(right, optionalArgument)
@@ -307,17 +307,17 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val value = s"$expName$k" -> getJoinResults(leftResult, rightResult, l, r)
iteratorsCombinations += value
})
- case _ => throw new Exception("Cannot use an iterator expression for the join condition clause. Right condition is not a plain expression")
+ case _ => throw RDFGenerationError("Cannot use an iterator expression for the join condition clause. Right condition is not a plain expression", parserInfo)
}
- case _ => throw new Exception("Cannot use an iterator expression for the join condition clause. Left condition is not a plain expression")
+ case _ => throw RDFGenerationError("Cannot use an iterator expression for the join condition clause. Left condition is not a plain expression", parserInfo)
}
- case _ => throw new Exception("Cannot join iterator with non iterator expression. Right clause is not an iterator expression")
+ case _ => throw RDFGenerationError("Cannot join iterator with non iterator expression. Right clause is not an iterator expression", parserInfo)
}
- case _ => throw new Exception("Cannot join iterator with non iterator expression. Left clause is not an iterator expression")
+ case _ => throw RDFGenerationError("Cannot join iterator with non iterator expression. Left clause is not an iterator expression", parserInfo)
}
}
- case StringOperation(left, right, unionString) => {
+ case StringOperation(left, right, unionString, parserInfo) => {
val expName = optionalArgument.asInstanceOf[Map[String, Any]].getOrElse("varName", "")
val leftList = doVisit(left, optionalArgument)
val rightList = doVisit(right, optionalArgument)
@@ -329,7 +329,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val value = s"$expName$k" -> getStringOperationResults(leftResult, rightResult, unionString)
iteratorsCombinations += value
})
- case _ => throw new Exception("Cannot make string operation with left clause being an iterator expression and right clause a non iterator expression")
+ case _ => throw RDFGenerationError("Cannot make string operation with left clause being an iterator expression and right clause a non iterator expression", parserInfo)
}
case left: List[Result] => getStringOperationResults(left, rightList.asInstanceOf[List[Result]], unionString)
}
@@ -360,13 +360,13 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
case None => {
val iteratorName = varList.tail.head.name
val values = varTable.keys.filter {
- case Var(name) => name.contains(iteratorName)
+ case Var(name, _) => name.contains(iteratorName)
case _ => false
}.toList.sortWith {
case (a: Var, b: Var) => a.name.size < b.name.size
}.map {
case v: Var => v.name.replaceFirst(iteratorName, "") -> {
- val vars = v.name.split("[.]").map(Var.apply).toList
+ val vars = v.name.split("[.]").map(Var(_, v.parserInfo)).toList
if (vars.size > 1)
doIteratorQuery(vars, middleArguments, file.asInstanceOf[LoadedSource])
else Nil
@@ -383,11 +383,13 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
}
} else if (varTable(varList.head).isInstanceOf[Exp] && varList.size > 1) {
- iteratorsCombinations(varList.map(_.name).mkString("."))
+ val composedVar = varList.map(_.name).mkString(".")
+ iteratorsCombinations.getOrElse(composedVar,
+ throw RDFGenerationError(s"Variable $composedVar does not resolve to any reference, check the used variable or the iterator definition", i.composedVar.parserInfo))
} else if (varList.size >= 3) {
doIteratorQuery(varList.slice(1, varList.size), middleArguments, file.asInstanceOf[LoadedSource])
} else {
- throw new Exception("Bad number of vars")
+ throw RDFGenerationError("Bad number of vars", i.parserInfo)
}
})
val finalResult = results.headOption match {
@@ -409,13 +411,13 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
case Some(executor) => executor
case None =>
val loadedSource = functionsIRI match {
- case JdbcURL(jdbcURL) => throw new Exception(s"The JDBC URL $jdbcURL cannot be used as the source of functions.")
+ case JdbcURL(jdbcURL, _) => throw RDFGenerationError(s"The JDBC URL $jdbcURL cannot be used as the source of functions.", f.parserInfo)
case fp: FilePath => doVisit(fp, optionalArgument).asInstanceOf[List[LoadedSource]].headOption match {
case Some(ls) => ls
- case None => throw new Exception(s"There is no functions code in the provided path: ${fp.value}")
+ case None => throw RDFGenerationError(s"There is no functions code in the provided path: ${fp.value}", f.parserInfo)
}
}
- val executor = new FunctionHubExecutor(loadedSource)
+ val executor = new FunctionHubExecutor(loadedSource, f.parserInfo)
functionHubExecuterCache.save(functionsIRI.value, executor)
executor
}
@@ -427,7 +429,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val sizeArguments = argumentsResults.filter(_.forall(_.isInstanceOf[Result])).map(_.size)
val maxSize = if(sizeArguments.isEmpty) 1 else argumentsResults.filter(_.forall(_.isInstanceOf[Result])).map(_.size).max
val sameSizeArguments = argumentsResults.exists(rl => rl.forall(r => r.isInstanceOf[Result]) && rl.size != maxSize)
- if(sameSizeArguments) throw new Exception(s"The results of the function arguments are not equal in size. This prevents a correct execution of the function.")
+ if(sameSizeArguments) throw RDFGenerationError(s"The results of the function arguments are not equal in size. This prevents a correct execution of the function.", f.parserInfo)
val argumentsHead = argumentsResults.find(_.forall(_.isInstanceOf[Result]))
val argumentsResultsFinal = argumentsResults.map(r => {
if(r.nonEmpty && r.head.isInstanceOf[ResultAutoIncrement]) {
@@ -471,7 +473,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
doVisit(varTable(sv), optionalArgument)
}
- case JsonPath(query) => {
+ case JsonPath(query, _) => {
logger.debug(s"Doing JSONPath query: $query")
val arguments = optionalArgument.asInstanceOf[Map[String, Any]]
val iteratorQuery = arguments.getOrElse("iteratorQuery", "").toString
@@ -509,7 +511,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
}
- case XmlPath(query) => {
+ case XmlPath(query, _) => {
logger.debug(s"Doing XPath query: $query")
val arguments = optionalArgument.asInstanceOf[Map[String, Any]]
val iteratorQuery = arguments.getOrElse("iteratorQuery", "").toString
@@ -544,31 +546,31 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
ResultAutoIncrement(a, "", "", None, None)
}
- case Matchers(_, matchers) => {
+ case Matchers(_, matchers, _) => {
val listToMatch = optionalArgument.asInstanceOf[List[Result]]
listToMatch.map(r => visitor.Result(r.id, r.rootIds, r.results.map(s => {
matchers.matchers.foldLeft(s)((value, matcher) => doVisit(matcher, value).asInstanceOf[String])
}), r.dataType, r.langTag, r.rdfCollection))
}
- case Matcher(replacedStrings, replacement) => {
+ case Matcher(replacedStrings, replacement, _) => {
if(replacedStrings.strings.contains(optionalArgument.toString)) replacement else optionalArgument.toString
}
- case LiteralSubject(prefix, value) => {
+ case LiteralSubject(prefix, value, _) => {
List(Result(Option.empty, HashSet(), List(value), None, None, None))
}
- case LiteralObject(prefix, value) => {
+ case LiteralObject(prefix, value, _) => {
val prefixValue = prefixTable(prefix.name)
List(Result(Option.empty, HashSet(), List(prefixValue + value), None, None, None))
}
- case LiteralObjectValue(value) => {
+ case LiteralObjectValue(value, _) => {
List(Result(Option.empty, HashSet(), List(value), None, None, None))
}
- case DataTypeGeneration(prefix, action, matcher) => {
+ case DataTypeGeneration(prefix, action, matcher, _) => {
val result = doVisit(action, optionalArgument)
val matchedResultList = matcher match {
case Some(matcherVar) => doVisit(matcherVar, result)
@@ -577,14 +579,14 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
result match {
case _: List[Result] =>
matchedResultList.asInstanceOf[List[Result]].map(result => {
- val newResults = result.results.map(prefixTable.getOrElse(prefix, "") + _)
+ val newResults = result.results.map(prefix.flatMap(p => prefixTable.get(p.name)).getOrElse("") + _)
Result(result.id, result.rootIds, newResults, None, None, None)
})
case _ => result
}
}
- case LangTagGeneration(action, matcher) => {
+ case LangTagGeneration(action, matcher, _) => {
val result = doVisit(action, optionalArgument)
val matchedResultList = matcher match {
case Some(matcherVar) => doVisit(matcherVar, result)
@@ -593,30 +595,30 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
matchedResultList
}
- case DataTypeLiteral(value) => {
+ case DataTypeLiteral(value, _) => {
value
}
- case LangTagLiteral(value) => {
+ case LangTagLiteral(value, _) => {
value
}
- case ShapeLink(shapeVar) => {
+ case ShapeLink(shapeVar, _) => {
doVisit(shapeVar, optionalArgument)
}
- case URL(url) =>
+ case URL(url, parserInfo) =>
logger.debug(s"Selecting file $url")
if(isRDFSource(url))
List(LoadedSource("", url))
else if(url.contains('*') && url.startsWith("file://"))
getAllFilesContents(url)
else if(url.contains('*'))
- throw new Exception("* wildcard not allowed over remote files")
+ throw RDFGenerationError("* wildcard not allowed over remote files", parserInfo)
else
List(new SourceHelper().getURLContent(url))
- case RelativePath(path) =>
+ case RelativePath(path, _) =>
if(isRDFSource(path)) {
val fileAbsolutePath = new File(path).getAbsolutePath
val fileProtocol =
@@ -627,9 +629,9 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
else if(path.contains('*')) getAllFilesContents(path)
else List(new SourceHelper().getContentFromRelativePath(path))
- case JdbcURL(url) => List(LoadedSource("", url))
+ case JdbcURL(url, _) => List(LoadedSource("", url))
- case Stdin() => List(new SourceHelper().getStdinContents())
+ case Stdin(_) => List(new SourceHelper().getStdinContents())
case default => visit(default, optionalArgument)
@@ -719,8 +721,8 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
protected def doVisitIteratorQuery(nestedIterator: QueryClause, currentIterator: QueryClause, optionalArgument: Any): Result = nestedIterator match {
- case JsonPath(query) => doVisit(JsonPath(query + currentIterator.query), optionalArgument).asInstanceOf[Result]
- case XmlPath(query) => doVisit(XmlPath(query + currentIterator.query), optionalArgument).asInstanceOf[Result]
+ case JsonPath(query, parserInfo) => doVisit(JsonPath(query + currentIterator.query, parserInfo), optionalArgument).asInstanceOf[Result]
+ case XmlPath(query, parserInfo) => doVisit(XmlPath(query + currentIterator.query, parserInfo), optionalArgument).asInstanceOf[Result]
}
protected def iteratorQueryToList(i: IteratorQuery): List[Var] = i.composedVar match {
@@ -730,29 +732,29 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
protected def iteratorResultsToQueries(iteratorQueries: List[Resultable], query: QueryClause, rootIds: HashSet[Int], file: LoadedSource): List[QueryWithIndex] = iteratorQueries.flatMap({
case r: ResultWithIteratorQuery => r.results.indices.map(i => query match {
- case XmlPath(xpathQuery) => {
- val composedQuery = XmlPath(xpathQuery.replaceFirst("[*]", (i + 1).toString))
+ case XmlPath(xpathQuery, parserInfo) => {
+ val composedQuery = XmlPath(xpathQuery.replaceFirst("[*]", (i + 1).toString), parserInfo)
val rootId = (r.iteratorQuery + file.filepath + i.toString).hashCode
QueryWithIndex(i.toString, rootIds.+(rootId), composedQuery, r.iteratorQuery)
}
- case JsonPath(jsonpathQuery) => {
- val composedQuery = JsonPath(jsonpathQuery.replaceFirst("[*]", i.toString))
+ case JsonPath(jsonpathQuery, parserInfo) => {
+ val composedQuery = JsonPath(jsonpathQuery.replaceFirst("[*]", i.toString), parserInfo)
val rootId = (r.iteratorQuery + file.filepath + i.toString).hashCode
QueryWithIndex(i.toString, rootIds.+(rootId), composedQuery, r.iteratorQuery)
}
}).toList
case r: ResultWithNested => r.results.indices.flatMap(i => query match {
- case XmlPath(xpathQuery) => {
- val indicedQuery = XmlPath(xpathQuery.replaceFirst("[*]", (i + 1).toString))
+ case XmlPath(xpathQuery, parserInfo) => {
+ val indicedQuery = XmlPath(xpathQuery.replaceFirst("[*]", (i + 1).toString), parserInfo)
val rootId = (r.iteratorQuery + file.filepath + i.toString).hashCode
iteratorResultsToQueries(List(r.nestedResults(i)), indicedQuery, rootIds.+(rootId), file)
}
- case JsonPath(jsonpathQuery) => {
+ case JsonPath(jsonpathQuery, parserInfo) => {
val indicedQuery =
if(r.iteratorQuery.contains("[*]"))
- JsonPath(jsonpathQuery.replaceFirst("[*]", i.toString))
+ JsonPath(jsonpathQuery.replaceFirst("[*]", i.toString), parserInfo)
else
- JsonPath(jsonpathQuery)
+ JsonPath(jsonpathQuery, parserInfo)
val rootId = (r.iteratorQuery + file.filepath + i.toString).hashCode
iteratorResultsToQueries(List(r.nestedResults(i)), indicedQuery, rootIds.+(rootId), file)
}
@@ -766,7 +768,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val querySeparator = if(xs.isDefinedAt(0)
&& getQueryFromVarTable(Var(context + x.name + "." + xs.head.name)).query.startsWith("[")) ""
else "."
- val constructedQuery = JsonPath(j.query + querySeparator + generateFinalQuery(xs, context + x.name + ".", j).query)
+ val constructedQuery = JsonPath(j.query + querySeparator + generateFinalQuery(xs, context + x.name + ".", j).query, j.parserInfo)
getQueryFromVarTable(Var(context + varList.map(_.name).mkString("."))) match {
case f: FieldQuery => {
if(f.pushed) {
@@ -781,21 +783,21 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
case _ => constructedQuery
}
}
- case xp: XmlPath => XmlPath(xp.query + "[*]/" + generateFinalQuery(xs, context + x.name + ".", xp).query)
- case csv: CSVPerRow => CSVPerRow(generateFinalQuery(xs, context + x.name + ".", csv).query)
- case sql: Sql => SqlColumn(sql.query, generateFinalQuery(xs, context + x.name + ".", sql).query)
- case sp: Sparql => SparqlColumn(sp.query, generateFinalQuery(xs, context + x.name + ".", sp).query)
- case FieldQuery(query, pushed, popped) => rootQuery match {
+ case xp: XmlPath => XmlPath(xp.query + "[*]/" + generateFinalQuery(xs, context + x.name + ".", xp).query, xp.parserInfo)
+ case csv: CSVPerRow => CSVPerRow(generateFinalQuery(xs, context + x.name + ".", csv).query, csv.parserInfo)
+ case sql: Sql => SqlColumn(sql.query, generateFinalQuery(xs, context + x.name + ".", sql).query, sql.parserInfo)
+ case sp: Sparql => SparqlColumn(sp.query, generateFinalQuery(xs, context + x.name + ".", sp).query, sp.parserInfo)
+ case FieldQuery(query, pushed, popped, _) => rootQuery match {
case j: JsonPath => {
val querySeparator = if(xs.isDefinedAt(0)
&& getQueryFromVarTable(Var(context + x.name + "." + xs.head.name)).query.startsWith("[")) ""
else "."
- JsonPath(query + querySeparator + generateFinalQuery(xs, context + x.name + ".", j).query)
+ JsonPath(query + querySeparator + generateFinalQuery(xs, context + x.name + ".", j).query, j.parserInfo)
}
- case xp: XmlPath => XmlPath(query + "[*]/" + generateFinalQuery(xs, context + x.name + ".", xp).query)
- case csv: CSVPerRow => CSVPerRow(query)
- case sql: Sql => SqlColumn(rootQuery.query, query)
- case sp: Sparql => SparqlColumn(rootQuery.query, query)
+ case xp: XmlPath => XmlPath(query + "[*]/" + generateFinalQuery(xs, context + x.name + ".", xp).query, xp.parserInfo)
+ case csv: CSVPerRow => CSVPerRow(query, csv.parserInfo)
+ case sql: Sql => SqlColumn(rootQuery.query, query, sql.parserInfo)
+ case sp: Sparql => SparqlColumn(rootQuery.query, query, sp.parserInfo)
}
}
@@ -806,11 +808,11 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
precedentQueries.map(q => {
val xpathIteratorQueryEnd = "[*]"
getQueryFromVarTable(Var(varContext + x.name)) match {
- case JsonPath(query) => (doVisit(JsonPath(q + query), arguments).asInstanceOf[Result], q + query)
- case XmlPath(query) => (doVisit(XmlPath(q + query), arguments).asInstanceOf[Result], q + query + xpathIteratorQueryEnd)
- case FieldQuery(query, _, _) => rootQuery match {
- case JsonPath(_) => (doVisit(JsonPath(q + query), arguments).asInstanceOf[Result], q + query)
- case XmlPath(_) => (doVisit(XmlPath(q + query), arguments).asInstanceOf[Result], q + query + xpathIteratorQueryEnd)
+ case JsonPath(query, parserInfo) => (doVisit(JsonPath(q + query, parserInfo), arguments).asInstanceOf[Result], q + query)
+ case XmlPath(query, parserInfo) => (doVisit(XmlPath(q + query, parserInfo), arguments).asInstanceOf[Result], q + query + xpathIteratorQueryEnd)
+ case FieldQuery(query, _, _, parserInfo) => rootQuery match {
+ case JsonPath(_, parserInfo) => (doVisit(JsonPath(q + query, parserInfo), arguments).asInstanceOf[Result], q + query)
+ case XmlPath(_, parserInfo) => (doVisit(XmlPath(q + query, parserInfo), arguments).asInstanceOf[Result], q + query + xpathIteratorQueryEnd)
}
}
})
@@ -818,14 +820,14 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
results.map(r => ResultWithIteratorQuery(r._1.id, r._1.rootIds, r._1.results, r._2))
case x :: xs => {
val queries = precedentQueries.map(q => getQueryFromVarTable(Var(varContext + x.name)) match {
- case JsonPath(query) => JsonPath(q + query)
- case XmlPath(query) => XmlPath(q + query + "[*]")
+ case JsonPath(query, parserInfo) => JsonPath(q + query, parserInfo)
+ case XmlPath(query, parserInfo) => XmlPath(q + query + "[*]", parserInfo)
})
val results = queries.map(doVisit(_, arguments).asInstanceOf[Result])
val newQueries = queries.indices.map(iq => {
results(iq).results.indices.map(ir => queries(iq) match {
- case JsonPath(query) => query.replaceFirst("[*]", ir.toString) + "."
- case XmlPath(query) => query.replaceFirst("[*]", (ir + 1).toString) + "/"
+ case JsonPath(query, _) => query.replaceFirst("[*]", ir.toString) + "."
+ case XmlPath(query, _) => query.replaceFirst("[*]", (ir + 1).toString) + "/"
})
}).toList
results.indices.map(r => ResultWithNested(results(r).id, results(r).rootIds, results(r).results,
@@ -837,7 +839,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val query = generateFinalQuery(iteratorVars, "", null)
query match {
case c: CSVPerRow => doPerRowResults(c, fileContentOrURL)
- case s: SqlColumn => doSqlResults(s, fileContentOrURL)
+ case s: SqlColumn => doSqlResults(s, fileContentOrURL, s.parserInfo)
case sp: SparqlColumn => doSparqlResults(sp, fileContentOrURL)
case _ => {
val iteratorQueries = doIteratorQueries(iteratorVars.slice(0, iteratorVars.size - 1), "", List(""), middleArguments, null)
@@ -886,12 +888,12 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
- private def doSqlResults(query: SqlColumn, source: LoadedSource): List[Result] = {
+ private def doSqlResults(query: SqlColumn, source: LoadedSource, parserInfo: ParserInfo): List[Result] = {
val dbURLConnection = source.filepath
val resultMap = queryResultCache.search(query.query, source) match {
case Some(value) => value.asInstanceOf[Map[String, List[String]]]
case None => {
- val connection = connectToDB(dbURLConnection)
+ val connection = connectToDB(dbURLConnection, parserInfo)
val statement = connection.prepareStatement(query.query)
val resultSet = statement.executeQuery()
queryResultCache.save(query.query, source, resultSet)
@@ -907,9 +909,9 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
composedResults.toList
}
- private def connectToDB(dbURL: String) = {
+ private def connectToDB(dbURL: String, parserInfo: ParserInfo) = {
logger.info(s"Connecting to JDBC database $dbURL")
- Class.forName(lookForJdbcDriver(dbURL, driversMap))
+ Class.forName(lookForJdbcDriver(dbURL, driversMap, parserInfo))
DriverManager.getConnection(dbURL, username, password)
}
@@ -927,7 +929,7 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
val id = (file.filepath + i).hashCode
Result(Option(id), HashSet(id), List(result), None, None, None)
}
- case None => throw new Exception("Field not present")
+ case None => throw RDFGenerationError("Field not present", query.parserInfo)
}
}
results.toList
@@ -1152,20 +1154,20 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
private def collectionConstructor(output: Model, nodes: Iterator[RDFNode],
rdfCollection: RDFCollection): Resource =
rdfCollection match {
- case RDFList() => output.createList(nodes.asJava)
- case RDFBag() =>
+ case RDFList(_) => output.createList(nodes.asJava)
+ case RDFBag(_) =>
val bag = output.createBag()
nodes.foreach(n => bag.add(n))
bag
- case RDFAlt() =>
+ case RDFAlt(_) =>
val alt = output.createAlt()
nodes.foreach(n => alt.add(n))
alt
- case RDFSeq() =>
+ case RDFSeq(_) =>
val seq = output.createSeq()
nodes.foreach(n => seq.add(n))
seq
- case _ => throw new Exception("Collection not supported")
+ case _ => throw RDFGenerationError(s"Collection ${rdfCollection.getClass.getName} not supported", rdfCollection.parserInfo)
}
private def normaliseDataType(datatype: Option[String]): Option[String] = datatype.map(dt => {
@@ -1190,12 +1192,12 @@ class RDFGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
protected def getShapePrefix(action: ActionOrLiteral): String = action match {
- case Action(shapePrefix, _, _) => shapePrefix
- case LiteralSubject(prefix, _) => prefix.name
+ case Action(shapePrefix, _, _, _) => shapePrefix.name
+ case LiteralSubject(prefix, _, _) => prefix.name
}
private def executeBuiltinFunction(result: Result, builtinFunction: BuiltinFunction): Result = builtinFunction match {
- case Index() => result.resultContext match {
+ case Index(_) => result.resultContext match {
case Some(rc) => Result(result.id, result.rootIds, result.results.map(_ => rc.index), result.dataType, result.langTag, result.rdfCollection)
case None => result
}
@@ -1364,4 +1366,4 @@ case class ResultAutoIncrement(iterator: AutoIncrement, predicate: String, names
List(predicateWithSpace + namespace + precedentString + iterator.iterator.next() + closingString)
}
}
-case class ResultContext(index: String, query: QueryClause, iteratorQuery: String)
\ No newline at end of file
+case class ResultContext(index: String, query: QueryClause, iteratorQuery: String)
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/RMLGeneratorVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/RMLGeneratorVisitor.scala
index 4566e41..1438a4a 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/RMLGeneratorVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/RMLGeneratorVisitor.scala
@@ -3,6 +3,7 @@ package com.herminiogarcia.shexml.visitor
import com.herminiogarcia.shexml.ast._
import com.typesafe.scalalogging.Logger
import org.apache.jena.query.Dataset
+import com.herminiogarcia.shexml.helper.RMLGenerationError
import org.apache.jena.rdf.model.{Resource, Statement}
class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult], prettify: Boolean ,username: String, password: String)
@@ -33,7 +34,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
override def doVisit(ast: AST, optionalArgument: Any): Any = ast match {
- case Shape(shapeName, action, predicateObjects, holdingGraph) => {
+ case Shape(shapeName, action, predicateObjects, holdingGraph, _) => {
logger.info(s"Converting shape ${shapeName.name} with ${predicateObjects.size} predicate-object statements under the graph " +
s"${holdingGraph.map(g => g.graphName.prefix + g.graphName.name).getOrElse("default")}")
val shapePrefix = getShapePrefix(action)
@@ -83,11 +84,11 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
})
}
- case Action(shapePrefix: String, action: ExpOrVar, _) => {
+ case Action(_, action: ExpOrVar, _, _) => {
doVisit(action, optionalArgument).asInstanceOf[List[RMLMap]].filter(_.logicalSource.nonEmpty)
}
- case IteratorQuery(firstVar, composedVar, _) => {
+ case IteratorQuery(firstVar, composedVar, _, _) => {
val receivedArguments = if(optionalArgument != null) optionalArgument.asInstanceOf[Map[String, Any]] else Map[String, Any]()
val arguments = if(receivedArguments.isDefinedAt("rmlType")) receivedArguments else receivedArguments.+("rmlType" -> "object")
varTable(firstVar) match {
@@ -95,7 +96,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
case i: IteratorQuery => List(doVisit(i, arguments + ("composedVar" -> composedVar)))
case source: FilePath => {
val iterator = composedVar match {
- case IteratorQuery(iteratorVar, _, _) =>
+ case IteratorQuery(iteratorVar, _, _, _) =>
if(varTable.get(iteratorVar).isDefined && iteratorVar.name.contains(".")) {
val query = transformNestedIterator(getQueryFromVarTable(iteratorVar), iteratorVar)
val rootQuery = getQueryFromVarTable(Var(iteratorVar.name.splitAt(iteratorVar.name.lastIndexOf("."))._1))
@@ -105,13 +106,13 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
else return RMLMap(Nil, Nil, Nil, Nil)
case v: Var => transformNestedIterator(getQueryFromVarTable(v), v)
}
- val logicalSourceName = mapPrefixOrBNode + "l_" + (source.toString + iterator.query).hashCode.abs
+ val logicalSourceName = mapPrefixOrBNode + "l_" + (source.value + iterator.query).hashCode.abs
val logicalSource = iterator match {
- case SqlQuery(query) => {
+ case SqlQuery(query, parserInfo) => {
val dbSubjectID = mapPrefixOrBNode + "db_" + dbIndex.next()
val datasource = List(
createStatementWithLiteral(dbSubjectID, rdfPrefix + "type", d2rqPrefix + "Database"),
- createStatementWithLiteral(dbSubjectID, d2rqPrefix + "jdbcDriver", lookForJdbcDriver(source.asInstanceOf[JdbcURL].value)),
+ createStatementWithLiteral(dbSubjectID, d2rqPrefix + "jdbcDriver", lookForJdbcDriver(source.asInstanceOf[JdbcURL].value, parserInfo = parserInfo)),
createStatementWithLiteral(dbSubjectID, d2rqPrefix + "jdbcDSN", source.asInstanceOf[JdbcURL].value),
createStatementWithLiteral(dbSubjectID, d2rqPrefix + "username", username),
createStatementWithLiteral(dbSubjectID, d2rqPrefix + "password", password)
@@ -128,9 +129,9 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
case _ => {
val referenceFormulation = iterator match {
- case JsonPath(_) => "JSONPath"
- case XmlPath(_) => "XPath"
- case CSVPerRow(_) => "CSV"
+ case JsonPath(_, _) => "JSONPath"
+ case XmlPath(_, _) => "XPath"
+ case CSVPerRow(_, _) => "CSV"
}
val iteratorStatement = if(iterator.isInstanceOf[JsonPath] | iterator.isInstanceOf[XmlPath])
List(createStatementWithLiteral(logicalSourceName, rmlPrefix + "iterator", iterator.query))
@@ -149,7 +150,11 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
case _ => arguments("composedVar") match {
case cv: Var => varTable(Var(v.name + "." + cv.name)).asInstanceOf[FieldQuery]
case i: IteratorQuery =>
- return doVisit(IteratorQuery(firstVar, IteratorQuery(Var(composedVar.asInstanceOf[Var].name + "." + i.firstVar.name), i.composedVar)), optionalArgument)
+ return doVisit(IteratorQuery(
+ firstVar,
+ IteratorQuery(Var(composedVar.asInstanceOf[Var].name + "." + i.firstVar.name), i.composedVar, parserInfo = i.parserInfo), parserInfo = i.parserInfo),
+ optionalArgument
+ )
}
}
@@ -172,11 +177,11 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
} else {
val datatypePrefix = arguments.get("dataType").map({
case dt: DataTypeLiteral => prefixTable(dt.value.split(":")(0) + ":")
- case _: DataTypeGeneration => throw new Exception("DataType generation from data not supported in RML")
+ case dtg: DataTypeGeneration => throw RMLGenerationError("DataType generation from data not supported in RML", dtg.parserInfo)
})
val datatype = arguments.get("dataType").map({
case dt: DataTypeLiteral => dt.value.split(":")(1)
- case _: DataTypeGeneration => throw new Exception("DataType generation from data not supported in RML")
+ case dtg: DataTypeGeneration => throw RMLGenerationError("DataType generation from data not supported in RML", dtg.parserInfo)
})
val datatypeURI = datatypePrefix.map(_ + datatype.get)
val langTagStatement = arguments.get("langTag").map({
@@ -234,7 +239,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
}
- case Union(left, right) => {
+ case Union(left, right, _) => {
val leftMap = doVisit(left, optionalArgument) match {
case l: List[RMLMap] => l
case r: RMLMap => List(r)
@@ -246,7 +251,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
leftMap.union(rightMap)
}
- case StringOperation(left, right, unionString) => {
+ case StringOperation(left, right, unionString, _) => {
val leftMap = doVisit(left, optionalArgument).asInstanceOf[RMLMap]
val rightMap = doVisit(right, optionalArgument).asInstanceOf[RMLMap]
val leftTemplate = leftMap.objectMap.filter(s => s.getPredicate.getLocalName == "template").head.getObject.asLiteral().getString
@@ -268,7 +273,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
)
}
- case Substitution(left, right, join) => {
+ case Substitution(left, right, join, _) => {
val leftMap = doVisit(left, optionalArgument).asInstanceOf[RMLMap]
val rightMap = doVisit(right, optionalArgument).asInstanceOf[RMLMap]
val joinMap = doVisit(join, optionalArgument).asInstanceOf[RMLMap]
@@ -314,7 +319,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
)
}
- case PredicateObject(predicate, objectOrShapeLink) => {
+ case PredicateObject(predicate, objectOrShapeLink, _) => {
val objectMap = doVisit(objectOrShapeLink, optionalArgument).asInstanceOf[List[RMLMap]]
objectMap.map(om => {
val predicateStatements = doVisit(predicate, optionalArgument).asInstanceOf[List[Statement]]
@@ -335,7 +340,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}).filter(_.isDefined).map {case Some(v) => v}
}
- case Predicate(prefix, extension) => {
+ case Predicate(prefix, extension, _) => {
val predicateID = mapPrefixOrBNode + "p_" + predicateIndex.next
List(
createStatement(predicateID, rdfPrefix + "type", rrPrefix + "predicateMap"),
@@ -343,21 +348,21 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
)
}
- case ObjectElement(prefix, action, literalValue, matcher, condition, dataType, langTag, rdfCollection) => {
+ case ObjectElement(prefix, action, literalValue, matcher, condition, dataType, langTag, rdfCollection, parserInfo) => {
val arguments = if(optionalArgument != null) optionalArgument.asInstanceOf[Map[String, Any]] else Map[String, Any]()
- val prefixArguments = if(prefix.nonEmpty) arguments.+("prefix" -> prefix) else arguments
+ val prefixArguments = if(prefix.nonEmpty) arguments.+("prefix" -> prefix.get.name) else arguments
val dataTypeArguments = if(dataType.isDefined) prefixArguments.+("dataType" -> dataType.getOrElse(None)) else prefixArguments
val finalArguments = if(langTag.isDefined) dataTypeArguments.+("langTag" -> langTag.getOrElse(None)) else dataTypeArguments
action match {
case Some(value) => doVisit(value, finalArguments)
case None => literalValue match {
case Some(literal) => doVisit(literal, finalArguments)
- case None => throw new Exception("No generation clause given.")
+ case None => throw RMLGenerationError("No generation clause given.", parserInfo)
}
}
}
- case LiteralObject(prefix, value) => {
+ case LiteralObject(prefix, value, _) => {
val fullPrefix = prefixTable(prefix.name)
val objectMapID = mapPrefixOrBNode + "o_" + objectIndex.next
val objectMap = List(
@@ -368,7 +373,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
List(RMLMap(Nil, objectMap, Nil, Nil))
}
- case LiteralSubject(prefix, value) => {
+ case LiteralSubject(prefix, value, _) => {
val fullPrefix = prefixTable(prefix.name)
val subjectMapID = mapPrefixOrBNode + "s_" + objectIndex.next
val objectMap = List(
@@ -379,7 +384,7 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
List(RMLMap(Nil, objectMap, Nil, Nil))
}
- case LiteralObjectValue(value) => {
+ case LiteralObjectValue(value, _) => {
val arguments = optionalArgument.asInstanceOf[Map[String, Any]]
val datatypePrefix = arguments.get("dataType").map(d => prefixTable(d.toString.split(":")(0) + ":"))
val datatype = arguments.get("dataType").map(d => d.toString.split(":")(1))
@@ -432,11 +437,11 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
private def transformNestedIterator(queryClause: QueryClause, v: Var): QueryClause = queryClause match {
- case FieldQuery(query, _, _) => getQueryFromVarTable(Var(v.name.split('.').head)) match {
- case JsonPath(jsonQuery) => JsonPath(jsonQuery + "." + query)
- case XmlPath(xpathQuery) => XmlPath(xpathQuery + "/" + query)
- case CSVPerRow(_) => CSVPerRow(query)
- case SqlQuery(_) => SqlQuery(query)
+ case FieldQuery(query, _, _, _) => getQueryFromVarTable(Var(v.name.split('.').head)) match {
+ case JsonPath(jsonQuery, parserInfo) => JsonPath(jsonQuery + "." + query, parserInfo)
+ case XmlPath(xpathQuery, parserInfo) => XmlPath(xpathQuery + "/" + query, parserInfo)
+ case CSVPerRow(_, parserInfo) => CSVPerRow(query, parserInfo)
+ case SqlQuery(_, parserInfo) => SqlQuery(query, parserInfo)
}
case j: JsonPath => j
case x: XmlPath => x
@@ -445,24 +450,24 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
private def mergeQueries(leftQuery: QueryClause, rightQuery: QueryClause): QueryClause = rightQuery match {
- case XmlPath(query) => XmlPath(leftQuery.query + "/" + query)
- case JsonPath(query) => JsonPath(leftQuery.query + "." + query)
+ case XmlPath(query, parserInfo) => XmlPath(leftQuery.query + "/" + query, parserInfo)
+ case JsonPath(query, parserInfo) => JsonPath(leftQuery.query + "." + query, parserInfo)
}
private def getNestedIteratorFieldQuery(iteratorQuery: IteratorQuery, precedentVar: Var, iteratorQueryClause: QueryClause): Option[FieldQuery] = {
iteratorQuery.composedVar match {
case i: IteratorQuery => {
val accIteratorQueryClause = iteratorQueryClause match {
- case JsonPath(_) => JsonPath(getQueryFromVarTable(Var(precedentVar.name + "." + i.firstVar.name)).query + ".")
- case XmlPath(_) => XmlPath(getQueryFromVarTable(Var(precedentVar.name + "." + i.firstVar.name)).query + "/")
+ case JsonPath(_, parserInfo) => JsonPath(getQueryFromVarTable(Var(precedentVar.name + "." + i.firstVar.name)).query + ".", parserInfo)
+ case XmlPath(_, parserInfo) => XmlPath(getQueryFromVarTable(Var(precedentVar.name + "." + i.firstVar.name)).query + "/", parserInfo)
}
getNestedIteratorFieldQuery(i, Var(precedentVar.name + "." + i.firstVar.name), accIteratorQueryClause)
}
case v: Var => varTable.get(Var(precedentVar.name + "." + v.name)) match {
case Some(value) => value match {
case fq: FieldQuery => iteratorQueryClause match {
- case JsonPath(_) => Some(FieldQuery(getQueryFromVarTable(precedentVar).query + "." + fq.query))
- case XmlPath(_) => Some(FieldQuery(getQueryFromVarTable(precedentVar).query + "/" + fq.query))
+ case JsonPath(_, parserInfo) => Some(FieldQuery(getQueryFromVarTable(precedentVar).query + "." + fq.query, parserInfo = parserInfo))
+ case XmlPath(_, parserInfo) => Some(FieldQuery(getQueryFromVarTable(precedentVar).query + "/" + fq.query, parserInfo = parserInfo))
}
case _ => None
}
@@ -493,4 +498,4 @@ class RMLGeneratorVisitor(dataset: Dataset, varTable: Map[Variable, VarResult],
}
case class RMLMap(logicalSource: List[Statement], objectMap: List[Statement],
- predicateMap: List[Statement], predicateObjectMap: List[Statement])
\ No newline at end of file
+ predicateMap: List[Statement], predicateObjectMap: List[Statement])
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/SemanticCheckerVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/SemanticCheckerVisitor.scala
new file mode 100644
index 0000000..cfd77f5
--- /dev/null
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/SemanticCheckerVisitor.scala
@@ -0,0 +1,93 @@
+package com.herminiogarcia.shexml.visitor
+
+import com.herminiogarcia.shexml.ast._
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+
+class SemanticCheckerVisitor(varTable: Map[Variable, VarResult]) extends DefaultVisitor[Any, Unit] {
+
+ override def doVisit(ast: AST, optionalArgument: Any): Unit = ast match {
+
+ case i: IteratorQuery => {
+ checkVar(i.firstVar, i.firstVar.parserInfo) //file var or existing expression
+ varTable(i.firstVar) match {
+ case e: Exp => doVisit(e, optionalArgument)
+ case _ =>
+ val fullName = iteratorQueryToList(i.composedVar).map(_.name).mkString(".")
+ varTable.getOrElse(Var(fullName), throw SemanticCheckerError(s"Variable $fullName is not defined", i.composedVar.parserInfo))
+ }
+ }
+
+ case LiteralObject(prefix, _, parserInfo) => checkVar(prefix, parserInfo)
+
+ case PredicateObject(predicate, objectOrShapeLink, _) => {
+ doVisit(predicate, optionalArgument)
+ doVisit(objectOrShapeLink, optionalArgument)
+ }
+
+ case Predicate(prefix, _, parserInfo) => checkPrefix(prefix, parserInfo)
+
+ case FunctionCalling(functionHub, _, arguments, parserInfo) => {
+ checkVar(functionHub, functionHub.parserInfo)
+ doVisit(arguments, optionalArgument)
+ }
+
+ case Arguments(arguments, _) => arguments.foreach(checkExpOrVar(_, optionalArgument))
+
+ case Action(shapePrefix, action, condition, parserInfo) => {
+ if(shapePrefix.name != "_:") checkPrefix(shapePrefix.name, shapePrefix.parserInfo)
+ doVisit(action, optionalArgument)
+ condition.foreach(doVisit(_, optionalArgument))
+ }
+
+ case ObjectElement(prefix, action, _, matcher, filter, dataType, langTag, _, parserInfo) => {
+ prefix.foreach(p => checkPrefix(p.name, p.parserInfo))
+ action.foreach(checkExpOrVar(_, optionalArgument))
+ matcher.foreach(m => checkVar(m, m.parserInfo))
+ filter.foreach(checkExpOrVar(_, optionalArgument))
+ dataType.foreach(doVisit(_, optionalArgument))
+ langTag.foreach(doVisit(_, optionalArgument))
+ }
+
+ case ShapeLink(shape, parserInfo) => checkVar(shape, parserInfo)
+
+ case DataTypeGeneration(prefix, action, matcher, parserInfo) => {
+ prefix.foreach(p => checkPrefix(p.name, p.parserInfo))
+ checkExpOrVar(action, optionalArgument)
+ matcher.foreach(checkVar(_, parserInfo))
+ }
+
+ case LangTagGeneration(action, matcher, parserInfo) => {
+ checkExpOrVar(action, optionalArgument)
+ matcher.foreach(checkVar(_, parserInfo))
+ }
+
+ case default => super.visit(default, optionalArgument)
+ }
+
+ private def checkVar(variable: Variable, parserInfo: ParserInfo): Unit = {
+ varTable.getOrElse(variable, throw SemanticCheckerError(s"Variable ${nameOf(variable)} is not defined", parserInfo))
+ }
+
+ private def checkPrefix(prefix: String, parserInfo: ParserInfo): Unit = {
+ varTable.getOrElse(Var(prefix), throw SemanticCheckerError(s"Prefix $prefix is not defined", parserInfo))
+ }
+
+ private def checkExpOrVar(exp: ExpOrVar, optionalArgument: Any): Unit = exp match {
+ case v: Var => checkVar(v, v.parserInfo)
+ case default => doVisit(default, optionalArgument)
+ }
+
+ private def nameOf(variable: Variable): String = variable match {
+ case v: Var => v.name
+ case sv: ShapeVar => sv.name
+ case gv: GraphVar => gv.prefix + gv.name
+ }
+
+ private def iteratorQueryToList(varOrIteratorQuery: VarOrIteratorQuery): List[Var] = varOrIteratorQuery match {
+ case v: Var => List(v)
+ case c: IteratorQuery => List(c.firstVar) ::: iteratorQueryToList(c.composedVar)
+ }
+
+ override def doVisitDefault(): Unit = ()
+
+}
diff --git a/src/main/scala/com/herminiogarcia/shexml/visitor/VarTableBuilderVisitor.scala b/src/main/scala/com/herminiogarcia/shexml/visitor/VarTableBuilderVisitor.scala
index 65ad6cb..cfd4724 100644
--- a/src/main/scala/com/herminiogarcia/shexml/visitor/VarTableBuilderVisitor.scala
+++ b/src/main/scala/com/herminiogarcia/shexml/visitor/VarTableBuilderVisitor.scala
@@ -10,14 +10,14 @@ import scala.collection.mutable
class VarTableBuilderVisitor(val variableMap: mutable.HashMap[Variable, VarResult]) extends DefaultVisitor[Map[String, Any], Unit] {
override def doVisit(ast: AST, optionalArgument: Map[String, Any]): Unit = ast match {
- case Prefix(name, value) => variableMap += ((name, value))
- case Source(name, value) => variableMap += ((name, value))
- case Query(name, value) => variableMap += ((name, value))
- case Functions(name, value) => variableMap += ((name, value))
- case Expression(name, value) => variableMap += ((name, value))
- case Iterator(name, value, fields, iterators) => registerIterator(name, value, fields, iterators, optionalArgument)
- case NestedIterator(name, value, fields, iterators) => registerIterator(name, value, fields, iterators, optionalArgument)
- case Field(name, value, _, _) => {
+ case Prefix(name, value, _) => variableMap += ((name, value))
+ case Source(name, value, _) => variableMap += ((name, value))
+ case Query(name, value, _) => variableMap += ((name, value))
+ case Functions(name, value, _) => variableMap += ((name, value))
+ case Expression(name, value, _) => variableMap += ((name, value))
+ case Iterator(name, value, fields, iterators, _) => registerIterator(name, value, fields, iterators, optionalArgument)
+ case NestedIterator(name, value, fields, iterators, _) => registerIterator(name, value, fields, iterators, optionalArgument)
+ case Field(name, value, _, _, _) => {
val fieldName = if(optionalArgument("variable") == "") name.name else optionalArgument("variable").toString + "." + name.name
variableMap += ((Var(fieldName), value))
}
@@ -34,15 +34,15 @@ class VarTableBuilderVisitor(val variableMap: mutable.HashMap[Variable, VarResul
def registerIterator(name: Var, value: QueryOrVar, fields: List[Field], iterators: List[NestedIterator], optionalArgument: Map[String, Any]) = {
val iteratorName = if(optionalArgument("variable") == "") name.name else optionalArgument("variable").toString + "." + name.name
val finalValue = value match {
- case FieldQuery(query, pushed, popped) => optionalArgument("type") match {
- case XmlPath(_) => XmlPath(query)
- case JsonPath(_) => JsonPath(query)
+ case FieldQuery(query, pushed, popped, _) => optionalArgument("type") match {
+ case XmlPath(_, parserInfo) => XmlPath(query, parserInfo)
+ case JsonPath(_, parserInfo) => JsonPath(query, parserInfo)
}
case _ => value
}
variableMap += ((Var(iteratorName), finalValue))
val topIteratorType = value match {
- case FieldQuery(_, _, _) => optionalArgument("type").asInstanceOf[QueryClause]
+ case FieldQuery(_, _, _, _) => optionalArgument("type").asInstanceOf[QueryClause]
case x: XmlPath => x
case j: JsonPath => j
case c: CSVPerRow => c
diff --git a/src/test/scala/com/herminiogarcia/shexml/FilmsCLITest.scala b/src/test/scala/com/herminiogarcia/shexml/FilmsCLITest.scala
index c481380..07947df 100644
--- a/src/test/scala/com/herminiogarcia/shexml/FilmsCLITest.scala
+++ b/src/test/scala/com/herminiogarcia/shexml/FilmsCLITest.scala
@@ -200,7 +200,7 @@ class FilmsCLITest extends AnyFunSuite {
| rr:template "{name}" ;
| rr:termType rr:Literal .
|
- |map:l_353498812 a rml:LogicalSource ;
+ |map:l_1694368582 a rml:LogicalSource ;
| rml:iterator "$.films[*]" ;
| rml:referenceFormulation ql:JSONPath ;
| rml:source "https://shexml.herminiogarcia.com/files/films.json" .
@@ -209,7 +209,7 @@ class FilmsCLITest extends AnyFunSuite {
| rr:template "http://example.com/{@id}" .
|
|map:m_2 a rr:TriplesMap ;
- | rml:logicalSource map:l_353498812 ;
+ | rml:logicalSource map:l_1694368582 ;
| rr:predicateObjectMap map:po_5 , map:po_3 , map:po_13 , map:po_1 , map:po_11 , map:po_9 , map:po_7 ;
| rr:subjectMap map:s_2 .
|
@@ -231,7 +231,7 @@ class FilmsCLITest extends AnyFunSuite {
|map:p_1 a rr:predicateMap ;
| rr:constant .
|
- |map:l_1572684380 a rml:LogicalSource ;
+ |map:l_1409459066 a rml:LogicalSource ;
| rml:iterator "//film" ;
| rml:referenceFormulation ql:XPath ;
| rml:source "https://shexml.herminiogarcia.com/files/films.xml" .
@@ -241,7 +241,7 @@ class FilmsCLITest extends AnyFunSuite {
| rr:termType rr:IRI .
|
|map:m_1 a rr:TriplesMap ;
- | rml:logicalSource map:l_1572684380 ;
+ | rml:logicalSource map:l_1409459066 ;
| rr:predicateObjectMap map:po_12 , map:po_10 , map:po_4 , map:po_8 , map:po_1 , map:po_6 , map:po_2 ;
| rr:subjectMap map:s_1 .
|
diff --git a/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedConditionalGenerationTest.scala b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedConditionalGenerationTest.scala
new file mode 100644
index 0000000..647b016
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedConditionalGenerationTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.runtimeErrors
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.RDFGenerationError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedConditionalGenerationTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [films.name IF films.shouldGenerate] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined variable in a conditional generation expression") {
+ val error = intercept[RDFGenerationError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable films.shouldGenerate does not resolve to any reference, check the used variable or the iterator definition")
+ assert(error.getEnrichedErrorMessage(example).contains("44: :name [films.name IF films.[1m[4mshouldGenerate[22m[24m] ;"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedFunctionCallTest.scala b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedFunctionCallTest.scala
new file mode 100644
index 0000000..781172a
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedFunctionCallTest.scala
@@ -0,0 +1,83 @@
+package com.herminiogarcia.shexml.runtimeErrors
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.RDFGenerationError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedFunctionCallTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |FUNCTIONS helper
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [helper.toLowerCase(films.name)] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined function in function call is detected") {
+ val error = intercept[RDFGenerationError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Function toLowerCase was not found in source code src/test/resources/functions.scala")
+ assert(error.getEnrichedErrorMessage(example).contains("45: :name [[1m[4mhelper.toLowerCase(films.name[22m[24m)] ;"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedVariableInActionRightExpressionTest.scala b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedVariableInActionRightExpressionTest.scala
new file mode 100644
index 0000000..c18d720
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/runtimeErrors/UndefinedVariableInActionRightExpressionTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.runtimeErrors
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.RDFGenerationError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedVariableInActionRightExpressionTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ |PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.nonExistent] {
+ | :name [films.name] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined variable in an action expression is detected") {
+ val error = intercept[RDFGenerationError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable films.nonExistent does not resolve to any reference, check the used variable or the iterator definition")
+ assert(error.getEnrichedErrorMessage(example).contains("43: :Films :[films.[1m[4mnonExistent[22m[24m] {"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedExpressionInShapeTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedExpressionInShapeTest.scala
new file mode 100644
index 0000000..cb36bab
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedExpressionInShapeTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedExpressionInShapeTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [notfilms.name] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined expression in a shape is detected") {
+ val error = intercept[SemanticCheckerError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable notfilms is not defined")
+ assert(error.getEnrichedErrorMessage(example).contains("44: :name [[1m[4mnotfilms[22m[24m.name] ;"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldExpressionTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldExpressionTest.scala
new file mode 100644
index 0000000..a8111bc
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldExpressionTest.scala
@@ -0,0 +1,84 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedFieldExpressionTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ |PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |EXPRESSION goodExp
+ |EXPRESSION erroredExp
+ |
+ |:Films :[films.id] {
+ | :name [films.name] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined field within expression is detected") {
+ val error = intercept[SemanticCheckerError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable film_xml.country.name is not defined")
+ assert(error.getEnrichedErrorMessage(example).contains("43: EXPRESSION erroredExp "))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldInShapeTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldInShapeTest.scala
new file mode 100644
index 0000000..22ad555
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFieldInShapeTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedFieldInShapeTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [films_xml_file.film_xml.nonexistent] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined field in a shape is detected") {
+ val error = intercept[SemanticCheckerError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable film_xml.nonexistent is not defined")
+ assert(error.getEnrichedErrorMessage(example).contains("44: :name [films_xml_file.[1m[4mfilm_xml.nonexistent[22m[24m] ;"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFileInExpressionTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFileInExpressionTest.scala
new file mode 100644
index 0000000..eec06fe
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFileInExpressionTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedFileInExpressionTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [films.name] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined file in expression is detected") {
+ val error = intercept[SemanticCheckerError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable films_csv_file is not defined")
+ assert(error.getEnrichedErrorMessage(example).contains("41: EXPRESSION films <[1m[4mfilms_csv_file[22m[24m.film_xml UNION films_json_file.film_json>"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFunctionHubTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFunctionHubTest.scala
new file mode 100644
index 0000000..4ab2d4d
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedFunctionHubTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedFunctionHubTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ | ITERATOR actresses {
+ | FIELD name
+ | FIELD role
+ | FIELD film <../../@id>
+ | }
+ |}
+ |ITERATOR film_json {
+ | FIELD id
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music
+ | FIELD photography
+ | ITERATOR actors {
+ | FIELD name
+ | FIELD role
+ | }
+ |}
+ |EXPRESSION films
+ |
+ |:Films :[films.id] {
+ | :name [helper.toUpperCase(films.name)] ;
+ | :year [films.year] ;
+ | :country [films.country] ;
+ | :director [films.directors] ;
+ | :actor @:Actor ;
+ | :actor @:Actress ;
+ |}
+ |
+ |:Actor :[films.actors.name] {
+ | :name [films.actors.name] ;
+ | :appear_on :[films.actors.film] ;
+ |}
+ |
+ |:Actress :[films.actresses.name] {
+ | :name [films.actresses.name] ;
+ | :appear_on :[films.actresses.film] ;
+ |}
+ """.stripMargin
+
+ test("Undefined variable for a function hub before function call") {
+ val error = intercept[SemanticCheckerError] {
+ mappingLauncher.launchMapping(example)
+ }
+ assert(error.message == "Variable helper is not defined")
+ assert(error.getEnrichedErrorMessage(example).contains("44: :name [[1m[4mhelper[22m[24m.toUpperCase(films.name)] ;"))
+ }
+
+}
diff --git a/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedMatcherTest.scala b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedMatcherTest.scala
new file mode 100644
index 0000000..41d7f9d
--- /dev/null
+++ b/src/test/scala/com/herminiogarcia/shexml/semanticChecker/UndefinedMatcherTest.scala
@@ -0,0 +1,82 @@
+package com.herminiogarcia.shexml.semanticChecker
+
+import com.herminiogarcia.shexml.ParallelConfigInferenceDatatypesNormaliseURIsFixture
+import com.herminiogarcia.shexml.helper.SemanticCheckerError
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.must.Matchers
+
+class UndefinedMatcherTest extends AnyFunSuite
+ with Matchers with ParallelConfigInferenceDatatypesNormaliseURIsFixture {
+
+ private val example =
+ """
+ PREFIX :
+ |PREFIX dbr:
+ |PREFIX schema:
+ |SOURCE films_xml_file
+ |SOURCE films_json_file
+ |ITERATOR film_xml {
+ | FIELD id <@id>
+ | FIELD name
+ | FIELD year
+ | FIELD country
+ | FIELD directors
+ | FIELD screenwritters
+ | FIELD music