Christophe Debruyne пре 8 година
родитељ
комит
f62a6ccb44
60 измењених фајлова са 4175 додато и 2 уклоњено
  1. 1 1
      LICENSE
  2. 34 1
      README.md
  3. 85 0
      pom.xml
  4. 8 0
      src/log4j.properties
  5. 707 0
      src/r2rml.rdf
  6. 199 0
      src/r2rml/Main.java
  7. 53 0
      src/r2rml/database/DB.java
  8. 34 0
      src/r2rml/database/Row.java
  9. 71 0
      src/r2rml/database/Rows.java
  10. 122 0
      src/r2rml/engine/Configuration.java
  11. 58 0
      src/r2rml/engine/R2RML.java
  12. 18 0
      src/r2rml/engine/R2RMLException.java
  13. 92 0
      src/r2rml/engine/R2RMLProcessor.java
  14. 75 0
      src/r2rml/engine/R2RMLTypeMapper.java
  15. 66 0
      src/r2rml/model/GraphMap.java
  16. 89 0
      src/r2rml/model/Join.java
  17. 109 0
      src/r2rml/model/LogicalTable.java
  18. 134 0
      src/r2rml/model/ObjectMap.java
  19. 73 0
      src/r2rml/model/PredicateMap.java
  20. 158 0
      src/r2rml/model/PredicateObjectMap.java
  21. 46 0
      src/r2rml/model/R2RMLMapping.java
  22. 70 0
      src/r2rml/model/R2RMLMappingFactory.java
  23. 22 0
      src/r2rml/model/R2RMLResource.java
  24. 59 0
      src/r2rml/model/R2RMLUtil.java
  25. 81 0
      src/r2rml/model/RefObjectMap.java
  26. 113 0
      src/r2rml/model/SubjectMap.java
  27. 336 0
      src/r2rml/model/TermMap.java
  28. 346 0
      src/r2rml/model/TriplesMap.java
  29. 16 0
      test/resources/01.mapping.ttl
  30. 4 0
      test/resources/01.output.ttl
  31. 29 0
      test/resources/02.mapping.ttl
  32. 6 0
      test/resources/02.output.ttl
  33. 32 0
      test/resources/03.mapping.ttl
  34. 3 0
      test/resources/03.output.ttl
  35. 15 0
      test/resources/04.mapping.ttl
  36. 14 0
      test/resources/04.output.ttl
  37. 12 0
      test/resources/05.mapping.ttl
  38. 9 0
      test/resources/05.output.ttl
  39. 20 0
      test/resources/06.mapping.ttl
  40. 5 0
      test/resources/06.output.ttl
  41. 10 0
      test/resources/07.mapping.ttl
  42. 4 0
      test/resources/07.output.ttl
  43. 14 0
      test/resources/08.mapping.ttl
  44. 3 0
      test/resources/08.output.ttl
  45. 15 0
      test/resources/09.mapping.ttl
  46. 3 0
      test/resources/09.output.ttl
  47. 19 0
      test/resources/10.mapping.ttl
  48. 7 0
      test/resources/10.output.rdf
  49. 7 0
      test/resources/11.config.properties
  50. 4 0
      test/resources/12.compareoutput.ttl
  51. 4 0
      test/resources/12.config.properties
  52. 16 0
      test/resources/12.mapping.ttl
  53. 3 0
      test/resources/12.output.ttl
  54. 4 0
      test/resources/13.config.properties
  55. 17 0
      test/resources/13.mapping.ttl
  56. 2 0
      test/resources/13.output/default.ttl
  57. 2 0
      test/resources/13.output/http_foo_bar_bar_.ttl
  58. 141 0
      test/resources/mysql.sql
  59. 275 0
      test/test/TestR2RML.java
  60. 201 0
      test/test/TestR2RMLMySQL.java

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2016 CNGL
+Copyright (c) 2016 Christophe Debruyne, ADAPT Centre, Trinity College Dublin, Ireland
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 34 - 1
README.md

@@ -1 +1,34 @@
-# r2rml
+# R2RML Implementation
+
+## Building and using the code
+
+To build the project and copy its dependencies, execute
+
+```bash
+$ mvn package
+$ mvn dependency:copy-dependencies
+```
+
+The run the R2RML processor, execute the following command
+
+```bash
+$ java -jar r2rml-0.0.1-SNAPSHOT.jar config.properties
+```
+
+Where `config.properties` is a properties file containing:
+
+- connectionURL, a JDBC connection URL to a database (required)
+- user, username for the user connecting to the database
+- password, password for the user connecting to the database
+- mappingFile, the R2RML mapping file (required)
+- outputFile, the output file (required)
+- format, format of the output files (default "TURTLE")
+- filePerGraph, flag to write the different graphs in separate files (default "false")
+- baseIRI, used in resolving relative IRIs produced by the R2RML mapping
+
+When named graphs are used in the R2RML mapping, one should use serelizations that support graphs such as N-QUADS and TRIG. The use of other serializations formats (such as TURTLE) results in all triples of all graphs being written away to that file. When setting the flag `filePerGraph` to `true` for seralization formats that do not support graphs, however, the value for `outputFile` will be used to create a directory in which a file will be created for each graph in RDF dataset.
+
+## License
+This implementation of R2RML is written by [Christophe Debruyne](http://www.christophedebruyne.be/). 
+
+This code is copyrighted by [ADAPT - Trinity College Dublin](http://www.adaptcentre.ie/) and released under the [MIT license](http://opensource.org/licenses/MIT).

+ 85 - 0
pom.xml

@@ -0,0 +1,85 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>r2rml</groupId>
+	<artifactId>r2rml</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>R2RML Implementation</name>
+	<build>
+		<sourceDirectory>src</sourceDirectory>
+		<resources>
+			<resource>
+				<directory>src</directory>
+				<includes>
+					<include>**/*.properties</include>
+					<include>**/*.rdf</include>
+				</includes>
+			</resource>
+		</resources>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.3</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>dependency/</classpathPrefix>
+							<mainClass>r2rml.Main</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.jena</groupId>
+			<artifactId>jena-arq</artifactId>
+			<version>3.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.jena</groupId>
+			<artifactId>jena-core</artifactId>
+			<version>3.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.jena</groupId>
+			<artifactId>jena-iri</artifactId>
+			<version>3.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.11</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.derby</groupId>
+			<artifactId>derby</artifactId>
+			<version>10.12.1.1</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>5.1.25</version>
+		</dependency>
+		<dependency>
+			<groupId>postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+			<version>9.1-901.jdbc4</version>
+		</dependency>
+	</dependencies>
+	<organization>
+		<name>Adapt Centre, Trinity College Dublin</name>
+		<url>http://www.adaptcentre.ie/</url>
+	</organization>
+</project>

+ 8 - 0
src/log4j.properties

@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

+ 707 - 0
src/r2rml.rdf

@@ -0,0 +1,707 @@
+<?xml version="1.0"?>
+
+
+<!DOCTYPE rdf:RDF [
+    <!ENTITY r2rml "http://www.w3.org/ns/r2rml#" >
+    <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
+    <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
+    <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
+    <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
+]>
+
+
+<rdf:RDF xmlns="http://www.w3.org/ns/r2rml#"
+     xml:base="http://www.w3.org/ns/r2rml"
+     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+     xmlns:r2rml="http://www.w3.org/ns/r2rml#"
+     xmlns:owl="http://www.w3.org/2002/07/owl#"
+     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
+     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:dc="http://purl.org/dc/elements/1.1/"
+     xmlns:foaf="http://xmlns.com/foaf/0.1/"
+     xmlns:cc="http://creativecommons.org/ns#"     
+     xmlns:vann="http://purl.org/vocab/vann/"
+     xmlns:vaem="http://www.linkedmodel.org/schema/vaem#">
+     
+    <owl:Ontology rdf:about="http://www.w3.org/ns/r2rml#">
+            <dc:title xml:lang="en">R2RML vocabulary</dc:title>
+            <rdf:type rdf:resource="http://purl.org/vocommons/voaf#Vocabulary"/>
+            <dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2012-01-24</dc:issued>
+            <dc:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2012-07-16</dc:modified>
+            <dc:description xml:lang="en">RDB to RDF Mapping Language - Vocabulary</dc:description>
+            <dc:creator>
+                  <foaf:Person rdf:about="http://richard.cyganiak.de/foaf.rdf#cygri">
+                        <foaf:name>Richard Cyganiak</foaf:name>
+                  </foaf:Person>
+            </dc:creator>
+            <dc:contributor>
+                  <foaf:Person rdf:about="http://boris.villazon.terrazas.name">
+                        <foaf:name>Boris Villazon-Terrazas</foaf:name>
+                  </foaf:Person>
+            </dc:contributor>
+            <dc:contributor>
+                  <foaf:Person rdf:about="http://www.w3.org/People/Ivan/">
+                        <foaf:name>Ivan Herman</foaf:name>
+                  </foaf:Person> 
+            </dc:contributor>
+            <dc:publisher>
+                  <foaf:Organization rdf:about="http://www.w3.org/">
+                        <rdfs:label xml:lang="en">The World Wide Web Consortium (W3C)</rdfs:label>
+                        <vaem:acronym>W3C</vaem:acronym>
+                  </foaf:Organization>
+            </dc:publisher>
+            <cc:license rdf:resource="http://creativecommons.org/licenses/by/3.0/"/>
+            <vann:preferredNamespacePrefix>rr</vann:preferredNamespacePrefix>
+            <vann:preferredNamespaceUri>http://www.w3.org/ns/r2rml#</vann:preferredNamespaceUri>
+	</owl:Ontology>    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Annotation properties
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Datatypes
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Object Properties
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#class -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;class">
+        <rdfs:comment xml:lang="en">The subject value generated for a logical table row will be asserted as an instance of this RDFS class.</rdfs:comment>
+        <rdfs:range rdf:resource="&rdfs;Class"/>
+        <rdfs:domain rdf:resource="&r2rml;SubjectMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#constant -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;constant"/>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#datatype -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;datatype">
+        <rdfs:comment xml:lang="en">Specifies the datatype of the object component for the generated triple from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&rdfs;Datatype"/>
+        <rdfs:domain rdf:resource="&r2rml;ObjectMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#graph -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;graph">
+        <rdfs:comment xml:lang="en">An IRI reference for use as the graph name of all triples generated with the GraphMap.</rdfs:comment>
+        <rdfs:domain>
+            <owl:Class>
+                <owl:intersectionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;GraphMap"/>
+                    <owl:Restriction>
+                        <owl:onProperty rdf:resource="&r2rml;template"/>
+                        <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:maxQualifiedCardinality>
+                        <owl:onDataRange rdf:resource="&xsd;string"/>
+                    </owl:Restriction>
+                </owl:intersectionOf>
+            </owl:Class>
+        </rdfs:domain>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#graphMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;graphMap">
+        <rdfs:comment xml:lang="en">Specifies a GraphMap. When used with a SubjectMap element, all the RDF triples generated from a logical row will be stored in the specified named graph. Otherwise, the RDF triple generated using the (predicate, object) pair will be stored in the specified named graph.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;GraphMap"/>
+        <rdfs:domain>
+            <owl:Class>
+                <owl:unionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;PredicateObjectMap"/>
+                    <rdf:Description rdf:about="&r2rml;SubjectMap"/>
+                </owl:unionOf>
+            </owl:Class>
+        </rdfs:domain>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#joinCondition -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;joinCondition">
+        <rdfs:comment xml:lang="en">Specifies the join condition for joining the child logical table with the parent logical table of the foreign key constraint.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;Join"/>
+        <rdfs:domain rdf:resource="&r2rml;RefObjectMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#logicalTable -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;logicalTable">
+        <rdfs:comment xml:lang="en">Definition of logical table to be mapped.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;LogicalTable"/>
+        <rdfs:domain rdf:resource="&r2rml;TriplesMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#object -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;object">
+        <rdfs:comment xml:lang="en">Specifies the object for the generated triple from the logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&rdfs;Resource"/>
+        <rdfs:domain>
+            <owl:Class>
+                <owl:intersectionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;ObjectMap"/>
+                    <owl:Restriction>
+                        <owl:onProperty rdf:resource="&r2rml;column"/>
+                        <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:maxQualifiedCardinality>
+                        <owl:onDataRange rdf:resource="&xsd;string"/>
+                    </owl:Restriction>
+                </owl:intersectionOf>
+            </owl:Class>
+        </rdfs:domain>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#objectMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;objectMap">
+        <rdfs:comment xml:lang="en">An ObjectMap element to generate the object component of the (predicate, object) pair from a logical table row.</rdfs:comment>
+        <rdfs:domain rdf:resource="&r2rml;PredicateObjectMap"/>
+        <rdfs:range>
+            <owl:Class>
+                <owl:unionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;ObjectMap"/>
+                    <rdf:Description rdf:about="&r2rml;RefObjectMap"/>
+                </owl:unionOf>
+            </owl:Class>
+        </rdfs:range>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#parentTriplesMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;parentTriplesMap">
+        <rdfs:comment xml:lang="en">Specifies the TriplesMap element corresponding to the parent logical table of the foreign key constraint.</rdfs:comment>
+        <rdfs:domain rdf:resource="&r2rml;RefObjectMap"/>
+        <rdfs:range rdf:resource="&r2rml;TriplesMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#predicate -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;predicate">
+        <rdfs:comment xml:lang="en">Specifies the predicate for the generated triple from the logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&rdf;Property"/>
+        <rdfs:domain>
+            <owl:Class>
+                <owl:intersectionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;PredicateMap"/>
+                    <owl:Restriction>
+                        <owl:onProperty rdf:resource="&r2rml;column"/>
+                        <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:maxQualifiedCardinality>
+                        <owl:onDataRange rdf:resource="&xsd;string"/>
+                    </owl:Restriction>
+                </owl:intersectionOf>
+            </owl:Class>
+        </rdfs:domain>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#predicateMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;predicateMap">
+        <rdfs:comment xml:lang="en">A PredicateMap element to generate the predicate component of the (predicate, object) pair from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;PredicateMap"/>
+        <rdfs:domain rdf:resource="&r2rml;PredicateObjectMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#predicateObjectMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;predicateObjectMap">
+        <rdfs:comment xml:lang="en">A PredicateObjectMap element to generate (predicate, object) pair from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;PredicateObjectMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#sqlVersion -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;sqlVersion">
+        <rdfs:comment xml:lang="en">An identifier for a SQL version.</rdfs:comment>
+        <rdfs:domain rdf:resource="&r2rml;R2RMLView"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#subject -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;subject">
+        <rdfs:comment xml:lang="en">An IRI reference for use as subject for all the RDF triples generated from a logical table row.</rdfs:comment>
+        <rdfs:domain>
+            <owl:Class>
+                <owl:intersectionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;SubjectMap"/>
+                    <owl:Restriction>
+                        <owl:onProperty rdf:resource="&r2rml;column"/>
+                        <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:maxQualifiedCardinality>
+                        <owl:onDataRange rdf:resource="&xsd;string"/>
+                    </owl:Restriction>
+                </owl:intersectionOf>
+            </owl:Class>
+        </rdfs:domain>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#subjectMap -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;subjectMap">
+        <rdfs:comment xml:lang="en">A SubjectMap element to generate a subject from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&r2rml;SubjectMap"/>
+        <rdfs:domain rdf:resource="&r2rml;TriplesMap"/>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#termType -->
+
+    <owl:ObjectProperty rdf:about="&r2rml;termType">
+        <rdfs:comment xml:lang="en">A string indicating whether subject or object generated using the value from column name specified for rr:column should be an IRI reference, blank node, or a literal.</rdfs:comment>
+        <rdfs:domain rdf:resource="&r2rml;TermMap"/>
+        <rdfs:range>
+            <owl:Class>
+                <owl:unionOf rdf:parseType="Collection">
+                    <rdf:Description rdf:about="&r2rml;BlankNode"/>
+                    <rdf:Description rdf:about="&r2rml;IRI"/>
+                    <rdf:Description rdf:about="&r2rml;Literal"/>
+                </owl:unionOf>
+            </owl:Class>
+        </rdfs:range>
+    </owl:ObjectProperty>
+    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Data properties
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#child -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;child">
+        <rdfs:comment xml:lang="en">Names a column in the child table of a join.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;Join"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#column -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;column">
+        <rdfs:comment xml:lang="en">Name of a column in the logical table. When generating RDF triples from a logical table row, value from the specified column is used as the subject, predicate, or object (based upon the specific domain).</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;TermMap"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#inverseExpression -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;inverseExpression">
+        <rdfs:comment xml:lang="en">An expression that allows, at query processing time, use of index-based access to the the (underlying) relational tables, instead of simply retrieving the table rows first and then applying a filter. This property is useful for retrieval based on conditions involving subject, predicate, or object generated from logical table column(s) and involves some transformation.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;TermMap"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#language -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;language">
+        <rdfs:comment xml:lang="en">Specified the language for the object component for the generated triple from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;ObjectMap"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#parent -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;parent">
+        <rdfs:comment xml:lang="en">Names a column in the parent table of a join.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;Join"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#sqlQuery -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;sqlQuery">
+        <rdfs:comment xml:lang="en">A valid SQL query.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;R2RMLView"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#tableName -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;tableName">
+        <rdfs:comment xml:lang="en">Schema-qualified name of a table or view.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;BaseTableOrView"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#template -->
+
+    <owl:DatatypeProperty rdf:about="&r2rml;template">
+        <rdfs:comment xml:lang="en">A template (format string) to specify how to generate a value for a subject, predicate, or object, using one or more columns from a logical table row.</rdfs:comment>
+        <rdfs:range rdf:resource="&xsd;string"/>
+        <rdfs:domain rdf:resource="&r2rml;TermMap"/>
+    </owl:DatatypeProperty>
+    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Classes
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- http://www.w3.org/1999/02/22-rdf-syntax-ns#Property -->
+
+    <owl:Class rdf:about="&rdf;Property"/>
+    
+
+
+    <!-- http://www.w3.org/2000/01/rdf-schema#Class -->
+
+    <owl:Class rdf:about="&rdfs;Class"/>
+    
+
+
+    <!-- http://www.w3.org/2000/01/rdf-schema#Datatype -->
+
+    <owl:Class rdf:about="&rdfs;Datatype"/>
+    
+
+
+    <!-- http://www.w3.org/2000/01/rdf-schema#Resource -->
+
+    <owl:Class rdf:about="&rdfs;Resource"/>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#BaseTableOrView -->
+
+    <owl:Class rdf:about="&r2rml;BaseTableOrView">
+        <rdfs:subClassOf rdf:resource="&r2rml;LogicalTable"/>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#BlankNode -->
+
+    <owl:Class rdf:about="&r2rml;BlankNode">
+        <rdfs:comment xml:lang="en">Denotes a blank node, used with termType</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#GraphMap -->
+
+    <owl:Class rdf:about="&r2rml;GraphMap">
+        <rdfs:subClassOf rdf:resource="&r2rml;TermMap"/>
+        <rdfs:comment xml:lang="en">Represents a graph map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#IRI -->
+
+    <owl:Class rdf:about="&r2rml;IRI">
+        <rdfs:comment xml:lang="en">Denotes an IRI, used with termpType.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#Join -->
+
+    <owl:Class rdf:about="&r2rml;Join">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;parent"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:minQualifiedCardinality>
+                <owl:onDataRange rdf:resource="&xsd;string"/>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;parent"/>
+                <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:maxQualifiedCardinality>
+                <owl:onDataRange rdf:resource="&xsd;string"/>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;child"/>
+                <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:maxQualifiedCardinality>
+                <owl:onDataRange rdf:resource="&xsd;string"/>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;child"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:minQualifiedCardinality>
+                <owl:onDataRange rdf:resource="&xsd;string"/>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents a join condition.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#Literal -->
+
+    <owl:Class rdf:about="&r2rml;Literal">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:comment xml:lang="en">Denotes a Literal, used with termType.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#LogicalTable -->
+
+    <owl:Class rdf:about="&r2rml;LogicalTable">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:comment xml:lang="en">Represents a logical table.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#ObjectMap -->
+
+    <owl:Class rdf:about="&r2rml;ObjectMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf rdf:resource="&r2rml;TermMap"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;object"/>
+                <owl:onClass rdf:resource="&rdfs;Resource"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:minQualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;object"/>
+                <owl:onClass rdf:resource="&rdfs;Resource"/>
+                <owl:maxQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:maxQualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;language"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:minQualifiedCardinality>
+                <owl:onDataRange rdf:resource="&xsd;string"/>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;datatype"/>
+                <owl:onClass rdf:resource="&rdfs;Datatype"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:minQualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents an object map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#PredicateMap -->
+
+    <owl:Class rdf:about="&r2rml;PredicateMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf rdf:resource="&r2rml;TermMap"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;predicate"/>
+                <owl:onClass rdf:resource="&rdf;Property"/>
+                <owl:qualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:qualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents a predicate map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#PredicateObjectMap -->
+
+    <owl:Class rdf:about="&r2rml;PredicateObjectMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;objectMap"/>
+                <owl:onClass rdf:resource="&r2rml;ObjectMap"/>
+                <owl:qualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:qualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;predicateMap"/>
+                <owl:onClass rdf:resource="&r2rml;PredicateMap"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:minQualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents a predicate-object map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#R2RMLView -->
+
+    <owl:Class rdf:about="&r2rml;R2RMLView">
+        <rdfs:subClassOf rdf:resource="&r2rml;LogicalTable"/>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#RefObjectMap -->
+
+    <owl:Class rdf:about="&r2rml;RefObjectMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:comment xml:lang="en">Denotes a reference to an object map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#SubjectMap -->
+
+    <owl:Class rdf:about="&r2rml;SubjectMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf rdf:resource="&r2rml;TermMap"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;class"/>
+                <owl:onClass rdf:resource="&rdfs;Class"/>
+                <owl:minQualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">0</owl:minQualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents a subject map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#TermMap -->
+
+    <owl:Class rdf:about="&r2rml;TermMap">
+        <rdfs:label xml:lang="en">Term Map</rdfs:label>
+        <rdfs:comment xml:lang="en">A function that generates an RDF term from a logical table row.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#TriplesMap -->
+
+    <owl:Class rdf:about="&r2rml;TriplesMap">
+        <rdfs:subClassOf rdf:resource="&owl;Thing"/>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;logicalTable"/>
+                <owl:onClass rdf:resource="&r2rml;LogicalTable"/>
+                <owl:qualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:qualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:subClassOf>
+            <owl:Restriction>
+                <owl:onProperty rdf:resource="&r2rml;subjectMap"/>
+                <owl:onClass rdf:resource="&r2rml;SubjectMap"/>
+                <owl:qualifiedCardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:qualifiedCardinality>
+            </owl:Restriction>
+        </rdfs:subClassOf>
+        <rdfs:comment xml:lang="en">Represents a triples map.</rdfs:comment>
+    </owl:Class>
+    
+
+
+    <!-- 
+    ///////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Individuals
+    //
+    ///////////////////////////////////////////////////////////////////////////////////////
+     -->
+
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#SQL2008 -->
+
+    <owl:NamedIndividual rdf:about="&r2rml;SQL2008">
+        <rdfs:comment xml:lang="en">Core SQL 2008</rdfs:comment>
+    </owl:NamedIndividual>
+    
+
+
+    <!-- http://www.w3.org/ns/r2rml#defaultGraph -->
+
+    <owl:NamedIndividual rdf:about="&r2rml;defaultGraph">
+        <rdfs:comment xml:lang="en">Denotes a default graph</rdfs:comment>
+    </owl:NamedIndividual>
+</rdf:RDF>
+
+
+
+<!-- Generated by the OWL API (version 3.2.3.1824) http://owlapi.sourceforge.net -->
+

+ 199 - 0
src/r2rml/Main.java

@@ -0,0 +1,199 @@
+package r2rml;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+import org.apache.jena.iri.IRIFactory;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+
+import r2rml.engine.Configuration;
+import r2rml.engine.R2RMLException;
+import r2rml.engine.R2RMLProcessor;
+
+/**
+ * Main Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class Main {
+
+	public static void main(String[] args) {
+
+		try {
+			if(args.length != 1) {
+				throw new R2RMLException("Only and exactly one config file needs to be passed as an argument", null);
+			}
+
+			Configuration configuration = new Configuration(args[0]);
+
+			if(configuration.getMappingFile() == null) {
+				throw new R2RMLException("A connection URL is mandatory.", null);
+			}
+
+			if(configuration.getMappingFile() == null) {
+				throw new R2RMLException("A R2RML mapping file is mandatory.", null);
+			}
+
+			R2RMLProcessor engine = new R2RMLProcessor(configuration);
+			engine.execute();
+
+			String format = configuration.getFormat();
+
+			if(format.equals("NQUADS") || format.equals("TRIG")) {
+				System.out.println("Writing dataset to dataset format. Ignoring irrelevant parameters.");
+				writeDatasetAsDatasetFile(configuration, engine);
+			} else if(configuration.isFilePerGraph()) {
+				System.out.println("Writing dataset as separate files. Ignoring irrelevant parameters.");
+				writeDatasetAsFiles(configuration, engine);
+			} else {
+				System.out.println("Writing dataset to one RDf file. Ignoring irrelevant parameters.");
+				writeDatasetAsFile(configuration, engine);
+			}
+		} catch (Exception e) {
+			System.err.println(e.getMessage());
+			System.exit(-1);
+		}
+	}
+
+	private static void writeDatasetAsFiles(
+			Configuration configuration, 
+			R2RMLProcessor engine) 
+					throws R2RMLException {
+		// Create RDF files out of RDF dataset (losing graphs)
+		try {
+			File o = new File(configuration.getOutputFile());
+			if(!o.exists()) o.mkdirs();
+			
+			Dataset ds = engine.getDataset();			
+			
+			String ext = getExtensionForFormat(configuration.getFormat());
+			File file = new File(o, "default" + ext);
+			writeModelToFile(ds.getDefaultModel(), file, configuration.getFormat());
+			
+			Iterator<String> graphs = ds.listNames();
+			while(graphs.hasNext()) {
+				String graph = graphs.next();
+				String name = IRIFactory.iriImplementation().construct(graph).toASCIIString();
+				name = name.replace("://", "_").replace("/", "_").replace(".", "_");
+				file = new File(o, name + ext);
+				writeModelToFile(ds.getNamedModel(graph), file, configuration.getFormat());
+			}
+			
+		} catch (Exception e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+	private static void writeModelToFile(
+			Model model, 
+			File file,  
+			String format) 
+					throws IOException {
+		
+		writeModelToFile(model, file, format, false);
+	}
+	
+	private static void writeModelToFile(
+			Model model, 
+			File file,
+			String format,
+			boolean append) 
+					throws IOException {
+		
+		FileWriter fw = new FileWriter(file, append);
+		BufferedWriter bw = new BufferedWriter(fw);
+		PrintWriter pw = new PrintWriter(bw);
+		
+		model.write(pw, format);
+		
+		pw.close();
+		bw.close();
+		fw.close();
+	}
+
+	private static void writeDatasetAsFile(
+			Configuration configuration, 
+			R2RMLProcessor engine) 
+					throws R2RMLException {
+		// Create RDF file out of RDF dataset (losing graphs)
+		try {
+			File o = new File(configuration.getOutputFile());
+			if(o.exists()) 
+				o.delete();
+			
+			Dataset ds = engine.getDataset();
+			writeModelToFile(ds.getDefaultModel(), o, configuration.getFormat());
+			
+			Iterator<String> graphs = engine.getDataset().listNames();
+			while(graphs.hasNext()) {
+				String graph = graphs.next();
+				writeModelToFile(ds.getNamedModel(graph), o, configuration.getFormat());
+			}
+
+		} catch (Exception e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+	private static void writeDatasetAsDatasetFile(
+			Configuration configuration, 
+			R2RMLProcessor engine)
+					throws R2RMLException {
+		try {
+			File o = new File(configuration.getOutputFile());
+			if(o.exists()) 
+				o.delete();
+
+			FileOutputStream out = new FileOutputStream(o);
+			Lang lang = configuration.getFormat().equals("NQUADS") ? Lang.NQ : Lang.TRIG;
+			RDFDataMgr.write(out, engine.getDataset(), lang);
+			out.close();
+
+		} catch (Exception e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * Simple method to find the corresponding extension for a format in case graphs
+	 * are written to different tiles
+	 * 
+	 * @param format
+	 * @return
+	 */
+	private static String getExtensionForFormat(String format) {
+		if(equals(format, new String[]{"turtle", "ttl"}))
+			return ".ttl";
+		if(equals(format, new String[]{"n-triples", "n-triple", "nt"}))
+			return ".nt";
+		if(equals(format, new String[]{"rdf", "rdf/xml", "rdf/xml-abbrev"}))
+			return ".rdf";
+		if(equals(format, new String[]{"n3"}))
+			return ".n3";
+		if(equals(format, new String[]{"rdf/json"}))
+			return ".rj";
+		if(equals(format, new String[]{"jsonld"}))
+			return ".jsonld";
+		
+		return null;
+	}
+
+	private static boolean equals(String format, String[] strings) {
+		String t = format == null ? "" : format.toLowerCase();
+		for(String s : strings) {
+			if(s.equals(t)) return true;
+		}
+		return false;
+	}
+
+}

+ 53 - 0
src/r2rml/database/DB.java

@@ -0,0 +1,53 @@
+package r2rml.database;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+import r2rml.engine.R2RMLException;
+
+/**
+ * DB Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class DB {
+
+	private Connection connection = null;
+	private List<Statement> statements = new ArrayList<Statement>();
+
+	public DB(Connection connection) {
+		this.connection = connection;
+	}
+
+	public Rows getRows(String query) throws R2RMLException {
+		try{
+			Statement statement = connection.createStatement();
+			statements.add(statement);
+			ResultSet resultset = statement.executeQuery(query);
+			return new Rows(resultset);
+		} catch(SQLException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+	public void close() throws R2RMLException {
+		if (!statements.isEmpty()) {
+			try {
+				for(Statement statement : statements) {
+					statement.close();
+				}
+				statements.clear();
+			} catch (SQLException e) {
+				throw new R2RMLException(e.getMessage(), e);
+			}
+		}
+		
+	}
+
+}

+ 34 - 0
src/r2rml/database/Row.java

@@ -0,0 +1,34 @@
+package r2rml.database;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import r2rml.engine.R2RMLException;
+
+/**
+ * Row Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class Row {
+
+	private ResultSet resultset = null;
+	private Map<String, Integer> indexMap = null;
+
+	protected Row(ResultSet resultset, Map<String, Integer> indexMap) {
+		this.resultset = resultset;
+		this.indexMap = indexMap;
+	}
+
+	public Object getObject(String column) throws R2RMLException {
+		try {
+			return resultset.getObject(indexMap.get(column));
+		} catch (SQLException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+}

+ 71 - 0
src/r2rml/database/Rows.java

@@ -0,0 +1,71 @@
+package r2rml.database;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import r2rml.engine.R2RMLException;
+
+/**
+ * Rows Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class Rows {
+
+	private ResultSet resultset = null;
+	private Map<String, Integer> indexMap = null;
+	private Map<String, Map<String, Integer>> projections = new HashMap<String, Map<String, Integer>>(); 
+	
+	public Rows(ResultSet resultset) throws R2RMLException {
+		this.resultset = resultset;
+		try {
+			indexMap = makeProjection(1, resultset.getMetaData().getColumnCount());
+		} catch (SQLException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+	private Map<String, Integer> makeProjection(int from, int to) throws R2RMLException {
+		String key = from + " " + to;
+		Map<String, Integer> indexMap = projections.get(key);
+		if(indexMap == null) {
+			indexMap = new HashMap<String, Integer>();
+			for(int i = from; i <= to; i++) {
+				try {
+					indexMap.put(resultset.getMetaData().getColumnLabel(i), i);
+				} catch (SQLException e) {
+					throw new R2RMLException(e.getMessage(), e);
+				}
+			}
+			projections.put(key, indexMap);
+		}
+		return indexMap;
+	}
+
+	public Row nextRow() throws R2RMLException {
+		try {
+			if(resultset.next())
+				return new Row(resultset, indexMap);
+		} catch (SQLException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+		return null;
+	}
+	
+	public Row projectCurrentRow(int from, int to) throws R2RMLException {
+		return new Row(resultset, makeProjection(from, to));
+	}
+	
+	public int getColumnCount() throws R2RMLException {
+		try {
+			return resultset.getMetaData().getColumnCount();
+		} catch (SQLException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+	}
+
+}

+ 122 - 0
src/r2rml/engine/Configuration.java

@@ -0,0 +1,122 @@
+package r2rml.engine;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Configuration Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class Configuration {
+	
+	private static Logger logger = Logger.getLogger(Configuration.class.getName());
+	
+	private String connectionURL = null;
+	private String user = null;
+	private String password = null;
+	private String mappingFile = null;
+	private String outputFile = null;
+	private String format = null;
+	private String baseIRI = null;
+	private boolean filePerGraph = false;
+	
+	public Configuration(String path) throws R2RMLException {
+		Properties properties = new Properties();
+		try {
+			properties.load(new FileInputStream(new File(path)));
+		} catch (IOException e) {
+			throw new R2RMLException(e.getMessage(), e);
+		}
+		
+		connectionURL = properties.getProperty("connectionURL");
+		user = properties.getProperty("user");
+		password = properties.getProperty("password");
+		mappingFile = properties.getProperty("mappingFile");
+		outputFile = properties.getProperty("outputFile");
+		format = properties.getProperty("format", "TURTLE");
+		setFilePerGraph("true".equals(properties.getProperty("filePerGraph", "false").toLowerCase()));
+		baseIRI = properties.getProperty("baseIRI");
+	}
+	
+	public Configuration() {
+		
+	}
+
+	public String getConnectionURL() {
+		return connectionURL;
+	}
+	
+	public void setConnectionURL(String connectionURL) {
+		this.connectionURL = connectionURL;
+	}
+	
+	public String getUser() {
+		return user;
+	}
+	
+	public void setUser(String user) {
+		this.user = user;
+	}
+	
+	public String getPassword() {
+		return password;
+	}
+	
+	public void setPassword(String password) {
+		this.password = password;
+	}
+	
+	public String getMappingFile() {
+		return mappingFile;
+	}
+	
+	public void setMappingFile(String mappingFile) {
+		this.mappingFile = mappingFile;
+	}
+	
+	public String getOutputFile() {
+		return outputFile;
+	}
+	
+	public void setOutputFile(String outputFile) {
+		this.outputFile = outputFile;
+	}
+	
+	public String getFormat() {
+		return format;
+	}
+	
+	public void setFormat(String format) {
+		this.format = format;
+	}
+
+	public String getBaseIRI() {
+		return baseIRI;
+	}
+
+	public void setBaseIRI(String baseIRI) {
+		if(baseIRI.contains("#"))
+			logger.warn("Base IRIs should not contain a \"#\".");
+		if(baseIRI.contains("?"))
+			logger.warn("Base IRIs should not contain a \"?\".");
+		if(!baseIRI.endsWith("/"))
+			logger.warn("Base IRIs should end with a \"/\".");
+		this.baseIRI = baseIRI;
+	}
+
+	public boolean isFilePerGraph() {
+		return filePerGraph;
+	}
+
+	public void setFilePerGraph(boolean filePerGraph) {
+		this.filePerGraph = filePerGraph;
+	}
+	
+}

+ 58 - 0
src/r2rml/engine/R2RML.java

@@ -0,0 +1,58 @@
+package r2rml.engine;
+
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
+
+/**
+ * R2RML Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public final class R2RML {
+	
+	public static final String NS = "http://www.w3.org/ns/r2rml#";
+	
+	public static final Resource BLANKNODE = ResourceFactory.createResource(NS + "BlankNode");
+	public static final Resource IRI = ResourceFactory.createResource(NS + "IRI");
+	public static final Resource LITERAL = ResourceFactory.createResource(NS + "Literal");
+	
+	// Special resource for default graph
+	public static final Resource defaultGraph = ResourceFactory.createResource(NS + "defaultGraph");
+
+	// Classes
+	public static final Resource BaseTableOrView = ResourceFactory.createResource(NS + "BaseTableOrView");
+	public static final Resource GraphMap = ResourceFactory.createResource(NS + "GraphMap");
+	public static final Resource LogicalTable = ResourceFactory.createResource(NS + "LogicalTable");
+	public static final Resource ObjectMap = ResourceFactory.createResource(NS + "ObjectMap");
+	public static final Resource R2RMLView = ResourceFactory.createResource(NS + "R2RMLView");
+	public static final Resource RefObjectMap = ResourceFactory.createResource(NS + "RefObjectMap");
+	public static final Resource SubjectMap = ResourceFactory.createResource(NS + "SubjectMap");
+	public static final Resource TermMap = ResourceFactory.createResource(NS + "TermMap");
+	public static final Resource TriplesMap = ResourceFactory.createResource(NS + "TriplesMap");
+	
+	// Properties
+	public static final Property child = ResourceFactory.createProperty(NS + "child");
+	public static final Property clazz = ResourceFactory.createProperty(NS + "class");
+	public static final Property column = ResourceFactory.createProperty(NS + "column");
+	public static final Property constant = ResourceFactory.createProperty(NS + "constant");
+	public static final Property datatype = ResourceFactory.createProperty(NS + "datatype");
+	public static final Property graphMap = ResourceFactory.createProperty(NS + "graphMap");
+	public static final Property joinCondition = ResourceFactory.createProperty(NS + "joinCondition");
+	public static final Property language = ResourceFactory.createProperty(NS + "language");
+	public static final Property logicalTable = ResourceFactory.createProperty(NS + "logicalTable");
+	public static final Property objectMap = ResourceFactory.createProperty(NS + "objectMap");
+	public static final Property parent = ResourceFactory.createProperty(NS + "parent");
+	public static final Property parentTriplesMap = ResourceFactory.createProperty(NS + "parentTriplesMap");
+	public static final Property predicateMap = ResourceFactory.createProperty(NS + "predicateMap");
+	public static final Property predicateObjectMap = ResourceFactory.createProperty(NS + "predicateObjectMap");
+	public static final Property sqlQuery = ResourceFactory.createProperty(NS + "sqlQuery");
+	public static final Property subject = ResourceFactory.createProperty(NS + "subject");
+	public static final Property subjectMap = ResourceFactory.createProperty(NS + "subjectMap");
+	public static final Property tableName = ResourceFactory.createProperty(NS + "tableName");
+	public static final Property template = ResourceFactory.createProperty(NS + "template");
+	public static final Property termType = ResourceFactory.createProperty(NS + "termType");
+	
+}

+ 18 - 0
src/r2rml/engine/R2RMLException.java

@@ -0,0 +1,18 @@
+package r2rml.engine;
+
+/**
+ * R2RMLException Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class R2RMLException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+	
+	public R2RMLException(String message, Throwable throwable) {
+		super(message, throwable);
+	}
+
+}

+ 92 - 0
src/r2rml/engine/R2RMLProcessor.java

@@ -0,0 +1,92 @@
+package r2rml.engine;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.log4j.Logger;
+
+import r2rml.database.DB;
+import r2rml.model.R2RMLMapping;
+import r2rml.model.R2RMLMappingFactory;
+
+/**
+ * R2RMLProcessor Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class R2RMLProcessor {
+
+	private static Logger logger = Logger.getLogger(R2RMLProcessor.class.getName());
+
+	private Configuration configuration = null;
+	private Connection connection = null;
+	private Dataset dataset = DatasetFactory.create();
+	
+	public R2RMLProcessor(Configuration configuration) {
+		this.configuration = configuration;
+	}
+
+	public void execute() {
+		createDatabaseConnection();
+		
+		String file = configuration.getMappingFile();
+		String baseIRI = configuration.getBaseIRI();
+		
+		R2RMLMapping mapping = R2RMLMappingFactory.createR2RMLMapping(file, baseIRI);
+		DB database = new DB(connection);
+		
+		boolean abort = false;
+		
+		if(mapping != null) {
+			if(!mapping.generateTriples(database, dataset))
+				abort = true;
+		} else {
+			abort = true;
+		}
+		
+		closeDatabaseConnection();
+
+		if(abort) {
+			logger.error("We had to abort generation of triples. See log for details.");
+		}
+	}
+
+	private void createDatabaseConnection() {
+		String user = configuration.getUser();
+		String pass = configuration.getPassword();
+		Properties props = new Properties();
+		if(user != null && !"".equals(user))
+			props.setProperty("user", user);
+		if(pass != null && !"".equals(pass))
+			props.setProperty("password", pass);
+
+		try {
+			connection = DriverManager.getConnection(configuration.getConnectionURL(), props);
+		} catch (SQLException e) {
+			logger.error("Error connecting to database.", e);
+			System.exit(-1);
+		}
+
+	}
+
+	private void closeDatabaseConnection() {
+		try {
+			if(!connection.isClosed())
+				connection.close();
+		} catch (SQLException e) {
+			logger.error("Error closing connection with database.", e);
+			System.exit(-1);
+		}
+
+	}
+
+	public Dataset getDataset() {
+		return dataset;
+	}
+}

+ 75 - 0
src/r2rml/engine/R2RMLTypeMapper.java

@@ -0,0 +1,75 @@
+package r2rml.engine;
+
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.datatypes.TypeMapper;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.rdf.model.Resource;
+
+/**
+ * R2RMLTypeMapper Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class R2RMLTypeMapper {
+
+	/**
+	 * Get an existing DataType or register one "on-the-fly"
+	 * 
+	 * @param datatypeDescription
+	 * @return
+	 */
+	public static RDFDatatype getTypeByName(Resource datatypeDescription) {
+		TypeMapper tm = TypeMapper.getInstance();
+		RDFDatatype datatype = tm.getTypeByName(datatypeDescription.getURI());
+		if(datatype == null) {
+			datatype = createNewDataTypeFor(datatypeDescription.getURI());
+			tm.registerDatatype(datatype);
+		}
+		return datatype;
+	}
+
+	private static RDFDatatype createNewDataTypeFor(String uri) {
+		return new RDFDatatype() {
+			
+			@Override
+			public String unparse(Object value) { return null; }
+			
+			@Override
+			public Object parse(String lexicalForm) throws DatatypeFormatException { return null; }
+			
+			@Override
+			public RDFDatatype normalizeSubType(Object value, RDFDatatype dt) { return null; }
+			
+			@Override
+			public boolean isValidValue(Object valueForm) { return false; }
+			
+			@Override
+			public boolean isValidLiteral(LiteralLabel lit) { return false; }
+			
+			@Override
+			public boolean isValid(String lexicalForm) { return false; }
+			
+			@Override
+			public boolean isEqual(LiteralLabel value1, LiteralLabel value2) { return false; }
+			
+			@Override
+			public String getURI() { return uri; }
+			
+			@Override
+			public Class<?> getJavaClass() { return null; }
+			
+			@Override
+			public int getHashCode(LiteralLabel lit) { return 0; }
+			
+			@Override
+			public Object extendedTypeDefinition() { return null; }
+			
+			@Override
+			public Object cannonicalise(Object value) { return null; }
+		};
+	}
+
+}

+ 66 - 0
src/r2rml/model/GraphMap.java

@@ -0,0 +1,66 @@
+package r2rml.model;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.log4j.Logger;
+
+import r2rml.database.Row;
+import r2rml.engine.R2RML;
+import r2rml.engine.R2RMLException;
+
+/**
+ * TriplesMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class GraphMap extends TermMap {
+
+	private static Logger logger = Logger.getLogger(GraphMap.class.getName());
+
+	public GraphMap(Resource description, String baseIRI) {
+		super(description, baseIRI);
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		if(!super.preProcessAndValidate())
+			return false;
+
+		return true;
+	}
+
+	@Override
+	protected RDFNode distillConstant(RDFNode node) {
+		// If the constant-valued term map is a subject map, predicate map or graph map, 
+		// then its constant value must be an IRI.
+		if(!node.isURIResource()) {
+			logger.error("Constant for GraphMap must be an IRI.");
+			logger.error(description);
+			return null;
+		}
+
+		return node;
+	}
+
+	@Override
+	protected boolean isChosenTermTypeValid() {
+		if(!isTermTypeIRI()) {
+			logger.error("TermType for GraphMap must be rr:IRI.");
+			logger.error(description);
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	protected Resource inferTermType() {
+		return R2RML.IRI;
+	}
+	
+	public Resource generateRDFTerm(Row row) throws R2RMLException {
+		return super.generateRDFTerm(row).asResource();
+	}
+
+}

+ 89 - 0
src/r2rml/model/Join.java

@@ -0,0 +1,89 @@
+package r2rml.model;
+
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * Join Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class Join extends R2RMLResource {
+
+	private static Logger logger = Logger.getLogger(Join.class.getName());
+
+	private String child;
+	private String parent;
+
+	public Join(Resource description) {
+		super(description);
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing Join " + description);
+
+		List<Statement> list = description.listProperties(R2RML.child).toList();
+		if(list.size() != 1) {
+			logger.error("Join must have exactly one rr:child.");
+			logger.error(description);
+			return false;
+		}
+
+		RDFNode node = list.get(0).getObject();
+		if(!node.isLiteral()) {
+			logger.error("rr:child has to be a literal.");
+			logger.error(description);
+			return false;
+		}
+
+		child = node.asLiteral().toString();
+
+		if(!R2RMLUtil.isValidColumnName(child)) {
+			logger.error("rr:child is not a valid column name.");
+			logger.error(description);
+			return false;
+		}
+		
+		list = description.listProperties(R2RML.parent).toList();
+		if(list.size() != 1) {
+			logger.error("Join must have exactly one rr:parent.");
+			logger.error(description);
+			return false;
+		}
+
+		node = list.get(0).getObject();
+		if(!node.isLiteral()) {
+			logger.error("rr:parent has to be a literal.");
+			logger.error(description);
+			return false;
+		}
+
+		parent = node.asLiteral().toString();
+
+		if(!R2RMLUtil.isValidColumnName(parent)) {
+			logger.error("rr:parent is not a valid column name.");
+			logger.error(description);
+			return false;
+		}
+
+		return true;
+	}
+
+	public String getChild() {
+		return child;
+	}
+
+	public String getParent() {
+		return parent;
+	}
+
+}

+ 109 - 0
src/r2rml/model/LogicalTable.java

@@ -0,0 +1,109 @@
+package r2rml.model;
+
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.XSD;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * LogicalTable Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class LogicalTable extends R2RMLResource {
+
+	private static Logger logger = Logger.getLogger(LogicalTable.class.getName());
+
+	private String tableName = null;
+	private String sqlQuery = null;
+
+	public LogicalTable(Resource description) {
+		super(description);
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing LogicalTable " + description);
+		
+		// Minimum conditions
+		// LogicalTable: Being one of its subclasses, rr:BaseTableOrView or rr:R2RMLView
+		// BaseTableOrView: Having an rr:tableName property
+		// R2RMLView: Having an rr:sqlQuery property
+
+		boolean isBaseTableOrView = false;
+		boolean isR2RMLView = false;
+		List<Statement> list = description.listProperties(RDF.type).toList();
+		for(Statement s : list) {
+			if(s.getObject().equals(R2RML.BaseTableOrView)) isBaseTableOrView = true;
+			if(s.getObject().equals(R2RML.R2RMLView)) isR2RMLView = true;
+		}
+
+		// We will not explicitly check whether it is a LogicalTable as it has
+		// to be one of its subclasses and -- via inference -- a LogicalTable.
+		// Perform XOR to see if it is exactly one of its subclasses.
+		if(!(isBaseTableOrView ^ isR2RMLView)){
+			logger.error("LogicalTable must be exactly one of its subclasses.");
+			logger.error(description);
+			return false;
+		}
+
+		if(isBaseTableOrView) {
+			// Check cardinality
+			List<Statement> l = description.listProperties(R2RML.tableName).toList();
+			if(l.size() != 1) {
+				logger.error("BaseTableOrView must have exactly one rr:tableName.");
+				logger.error(description);
+				return false;
+			}
+			
+			// Check value
+			RDFNode n = l.get(0).getObject();
+			
+			if(!n.isLiteral() || !n.asLiteral().getDatatype().getURI().equals(XSD.xstring.getURI())) {
+				logger.error("TableName is not a valid xsd:string.");
+				logger.error(description);
+				return false;
+			}
+			
+			// All clear
+			tableName = n.toString();
+			
+		} else 	{
+			// Check cardinality
+			List<Statement> l = description.listProperties(R2RML.sqlQuery).toList();
+			if(l.size() != 1) {
+				logger.error("R2RMLView must have exactly one rr:sqlQuery.");
+				logger.error(description);
+				return false;
+			}
+			
+			// Check value
+			RDFNode n = l.get(0).getObject();
+			if(!n.isLiteral() || !n.asLiteral().getDatatype().getURI().equals(XSD.xstring.getURI())) {
+				logger.error("SqlQuery is not a valid xsd:string.");
+				logger.error(description);
+				return false;
+			}
+			
+			// All clear
+			sqlQuery = n.toString().trim();
+		}
+
+		return true;
+	}
+
+	public String generateQuery() {
+		if(sqlQuery != null) 
+			return sqlQuery;
+		return "SELECT * FROM " + tableName;
+	}
+
+}

+ 134 - 0
src/r2rml/model/ObjectMap.java

@@ -0,0 +1,134 @@
+package r2rml.model;
+
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * ObjectMap Class.
+ * 
+ * TODO: Implement inferring datatypes
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class ObjectMap extends TermMap {
+
+	private static Logger logger = Logger.getLogger(ObjectMap.class.getName());
+	private List<Statement> datatypes = null;
+	private List<Statement> languages = null;
+
+	public ObjectMap(Resource description, String baseIRI) {
+		super(description, baseIRI);
+		datatypes = description.listProperties(R2RML.datatype).toList();
+		languages = description.listProperties(R2RML.language).toList();
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing ObjectMap " + description);
+
+		if(!super.preProcessAndValidate())
+			return false;
+
+		if(isTermTypeIRI() && (datatypes.size() > 0 || languages.size() > 0)) {
+			logger.error("TermType IRI cannot have a rr:datatype or rr:language.");
+			logger.error(description);
+			return false;
+		}
+
+		if(isTermTypeLiteral()) {
+			if(datatypes.size() > 1) {
+				logger.error("A TermMap must not have more than one rr:datatype value.");
+				logger.error(description);
+				return false;
+			}
+
+			if(languages.size() > 1) {
+				logger.error("A TermMap must not have more than one rr:language value.");
+				logger.error(description);
+				return false;
+			}
+
+			if(languages.size() == 1 && datatypes.size() == 1) {
+				logger.error("A TermMap cannot have both a rr:datatype and rr:language.");
+				logger.error(description);
+				return false;
+			}
+
+			if(languages.size() == 1) {
+				RDFNode node = languages.get(0).getObject();
+				if(!R2RMLUtil.isValidLanguageTag(node)) {
+					logger.error("The value of rr:language must be a valid language tag.");
+					logger.error(description);
+					return false;
+				}
+
+				language = node.asLiteral().toString();
+			}
+
+			if(datatypes.size() == 1) {
+				RDFNode node = datatypes.get(0).getObject();
+				if(!node.isURIResource()) {
+					logger.error("The value of rr:datatype must be an IRI.");
+					logger.error(description);
+					return false;
+				}
+
+				datatype = node.asResource();
+			}
+
+		}
+
+		return true;
+	}
+
+
+
+	@Override
+	protected RDFNode distillConstant(RDFNode node) {
+		// If the constant-valued term map is an object map, then its 
+		// constant value must be an IRI or literal.
+		if(isConstantValuedTermMap()) {
+			if(!node.isURIResource() && !node.isLiteral()) {
+				logger.error("Constant for ObjectMap must be an IRI or literal.");
+				logger.error(description);
+				return null;
+			}
+		}
+
+		return node;
+	}
+
+	@Override
+	protected boolean isChosenTermTypeValid() {
+		// Check if invalid URI was provided!
+		if(!(isTermTypeLiteral() || isTermTypeBlankNode() || isTermTypeIRI())) {
+			logger.error("TermType for ObjectMap must be rr:IRI, rr:Literal or rr:BlankNode.");
+			logger.error(description);
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	protected Resource inferTermType() {
+		/* rr:Literal, if it is an object map and at least one
+		 * of the following conditions is true:
+		 * - It is a column-based term map.
+		 * - It has a rr:language property.
+		 * - It has a rr:datatype property. 
+		 * rr:IRI, otherwise*/
+
+		if(isColumnValuedTermMap() || datatypes.size() > 0 || languages.size() > 0) 
+			return R2RML.LITERAL;
+
+		return R2RML.IRI;
+	}
+}

+ 73 - 0
src/r2rml/model/PredicateMap.java

@@ -0,0 +1,73 @@
+package r2rml.model;
+
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.apache.log4j.Logger;
+
+import r2rml.database.Row;
+import r2rml.engine.R2RML;
+import r2rml.engine.R2RMLException;
+
+/**
+ * PredicateMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class PredicateMap extends TermMap {
+
+	private static Logger logger = Logger.getLogger(PredicateMap.class.getName());
+
+	public PredicateMap(Resource description, String baseIRI) {
+		super(description, baseIRI);
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing PredicateMap " + description);
+		
+		if(!super.preProcessAndValidate())
+			return false;
+
+		return true;
+	}
+
+	@Override
+	protected RDFNode distillConstant(RDFNode node) {
+		// If the constant-valued term map is a subject map, predicate map or graph map, 
+		// then its constant value must be an IRI.
+		if(!node.isURIResource()) {
+			logger.error("Constant for PredicateMap must be an IRI.");
+			logger.error(description);
+			return null;
+		}
+
+		return node;
+	}
+
+	@Override
+	protected boolean isChosenTermTypeValid() {
+		if(!isTermTypeIRI()) {
+			logger.error("TermType for PredicateMap must be rr:IRI.");
+			logger.error(description);
+			return false;
+		}
+		
+		return true;
+	}
+
+	@Override
+	protected Resource inferTermType() {
+		return R2RML.IRI;
+	}
+	
+	public Property generateRDFTerm(Row row) throws R2RMLException {
+		// PredicateMaps generate terms that are properties.
+		String uri = super.generateRDFTerm(row).asResource().getURI();
+		return ResourceFactory.createProperty(uri);
+	}
+
+}

+ 158 - 0
src/r2rml/model/PredicateObjectMap.java

@@ -0,0 +1,158 @@
+package r2rml.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * PredicateObjectMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class PredicateObjectMap extends R2RMLResource {
+
+	private static Logger logger = Logger.getLogger(PredicateObjectMap.class.getName());
+
+	private List<GraphMap> graphMaps = new ArrayList<GraphMap>();
+	private List<PredicateMap> predicateMaps = new ArrayList<PredicateMap>();
+	private List<ObjectMap> objectMaps = new ArrayList<ObjectMap>();
+	private List<RefObjectMap> refObjectMaps = new ArrayList<RefObjectMap>();
+
+	private String baseIRI = null;
+
+	public PredicateObjectMap(Resource description, String baseIRI) {
+		super(description);
+		this.baseIRI  = baseIRI;
+	}
+
+	public List<PredicateMap> getPredicateMaps() {
+		return predicateMaps;
+	}
+
+	public List<ObjectMap> getObjectMaps() {
+		return objectMaps;
+	}
+
+	public List<RefObjectMap> getRefObjectMaps() {
+		return refObjectMaps;
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing PredicateObjectMap " + description);
+
+		/*
+		 * PredicateObjectMaps must have one or more PredicateMaps and one or
+		 * more ObjectMaps. Since we generate classes via the shorthands before
+		 * processing, we just check rr:predicateMap and rr:objectMap.
+		 */
+		// Check whether PredicateMaps exists
+		List<Statement> listp = description.listProperties(R2RML.predicateMap).toList();
+		if(listp.size() == 0) {
+			logger.error("PredicateObjectMap must have at least one rr:predicateMap.");
+			logger.error(description);
+			return false;
+		}
+
+		// Pre-process and validate each PredicateMap
+		for(Statement s : listp) {
+			if(!s.getObject().isResource()) {
+				logger.error("rr:predicateMap must refer to a resource.");
+				logger.error(description);
+				return false;
+			}
+			PredicateMap pm = new PredicateMap(s.getObject().asResource(), baseIRI);
+			if(!pm.preProcessAndValidate())
+				return false;
+			predicateMaps.add(pm);
+		}
+		
+		// Pre-process and validate each GraphMap
+		List<Statement> listg = description.listProperties(R2RML.graphMap).toList();
+		for(Statement s : listg) {
+			if(!s.getObject().isResource()) {
+				logger.error("rr:graphMap must refer to a resource.");
+				logger.error(description);
+				return false;
+			}
+			GraphMap gm = new GraphMap(s.getObject().asResource(), baseIRI);
+			if(!gm.preProcessAndValidate())
+				return false;
+			graphMaps.add(gm);
+		}
+
+		// Check whether ObjectMaps exists
+		List<Statement> listo = description.listProperties(R2RML.objectMap).toList();
+		if(listo.size() == 0) {
+			logger.error("PredicateObjectMap must have at least one rr:objectMap.");
+			logger.error(description);
+			return false;
+		}
+
+		// Pre-process and validate each ObjectMap or RefObjectMap
+		for(Statement s : listo) {
+			if(!s.getObject().isResource()) {
+				logger.error("rr:objectMap must refer to a resource.");
+				logger.error(description);
+				return false;
+			}
+
+			Resource r = s.getObject().asResource();
+
+			/*
+			 * Because of all the OWL axioms, it is difficult to infer
+			 * whether resources are a ObjectMap or a RefObjectMap. This 
+			 * is because after reasoning, those instances are members of
+			 * concepts that are OM or ROM concept union (!) something
+			 * else. One should be able to deduce that from the roles they
+			 * play however.
+			 * TODO: investigate if we can't configure the reasoner and execute more elegantly.
+			 */
+			boolean isOM = r.hasProperty(R2RML.column) 
+					|| r.hasProperty(R2RML.constant) 
+					|| r.hasProperty(R2RML.template);
+			boolean isROM = r.hasProperty(R2RML.joinCondition);
+
+			// If it plays the roles of a OM, create OM
+			if(isOM && !isROM) {
+				ObjectMap om = new ObjectMap(r, baseIRI);
+				if(!om.preProcessAndValidate())
+					return false;
+				objectMaps.add(om);
+			} 
+			// If it plays the role of a ROM, create ROM
+			else if(isROM && !isOM) {
+				RefObjectMap rom = new RefObjectMap(r);
+				if(!rom.preProcessAndValidate())
+					return false;
+				refObjectMaps.add(rom);
+			} 
+			// Can't be both!
+			else if (isOM && isROM){
+				logger.error("Resource cannot be both an ObjectMap or a RefObjectMap.");
+				logger.error(description);
+				return false;
+			} 
+			// Can't be neither...
+			else {
+				logger.error("rr:objectMap must refer to an ObjectMap or a RefObjectMap.");
+				logger.error(description);
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	public List<GraphMap> getGraphMaps() {
+		return graphMaps;
+	}
+
+}

+ 46 - 0
src/r2rml/model/R2RMLMapping.java

@@ -0,0 +1,46 @@
+package r2rml.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jena.query.Dataset;
+import org.apache.jena.rdf.model.Resource;
+
+import r2rml.database.DB;
+
+/**
+ * R2RMLMapping Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class R2RMLMapping {
+
+	private Map<Resource, TriplesMap> triplesMaps = new HashMap<Resource, TriplesMap>();
+
+	public void addTriplesMap(Resource triplesMapResource, TriplesMap triplesMap) {
+		triplesMaps.put(triplesMapResource , triplesMap);
+	}
+
+	public Map<Resource, TriplesMap> getTriplesMaps() {
+		return triplesMaps;
+	}
+
+	public void setTriplesMaps(Map<Resource, TriplesMap> triplesMaps) {
+		this.triplesMaps = triplesMaps;
+	}
+
+	public boolean generateTriples(DB database, Dataset dataset) {
+		for(TriplesMap tm : triplesMaps.values()) {
+			if(!tm.generateTriples(database, dataset, triplesMaps)){
+				// Something went wrong, abort.
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	
+
+}

+ 70 - 0
src/r2rml/model/R2RMLMappingFactory.java

@@ -0,0 +1,70 @@
+package r2rml.model;
+
+import java.util.List;
+
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.rdf.model.InfModel;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.util.FileManager;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * R2RMLMappingFactory Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class R2RMLMappingFactory {
+	
+	private static Logger logger = Logger.getLogger(R2RMLMappingFactory.class.getName());
+
+	private static String CONSTRUCTSMAPS = "PREFIX rr: <http://www.w3.org/ns/r2rml#> CONSTRUCT { ?x rr:subjectMap [ rr:constant ?y ]. } WHERE { ?x rr:subject ?y. }";
+	private static String CONSTRUCTOMAPS = "PREFIX rr: <http://www.w3.org/ns/r2rml#> CONSTRUCT { ?x rr:objectMap [ rr:constant ?y ]. } WHERE { ?x rr:object ?y. }";
+	private static String CONSTRUCTPMAPS = "PREFIX rr: <http://www.w3.org/ns/r2rml#> CONSTRUCT { ?x rr:predicateMap [ rr:constant ?y ]. } WHERE { ?x rr:predicate ?y. }";
+	private static String CONSTRUCTGMAPS = "PREFIX rr: <http://www.w3.org/ns/r2rml#> CONSTRUCT { ?x rr:graphMap [ rr:constant ?y ]. } WHERE { ?x rr:graph ?y. }";
+
+	public static R2RMLMapping createR2RMLMapping(String mappingFile, String baseIRI) {
+		R2RMLMapping mapping = new R2RMLMapping();
+		
+		// We reason over the mapping to facilitate retrieval of the mappings
+		Model data = FileManager.get().loadModel(mappingFile);
+
+		// We construct triples to replace the shortcuts.
+		data.add(QueryExecutionFactory.create(CONSTRUCTSMAPS, data).execConstruct());
+		data.add(QueryExecutionFactory.create(CONSTRUCTOMAPS, data).execConstruct());
+		data.add(QueryExecutionFactory.create(CONSTRUCTPMAPS, data).execConstruct());
+		data.add(QueryExecutionFactory.create(CONSTRUCTGMAPS, data).execConstruct());
+		
+		Model schema = ModelFactory.createDefaultModel();
+		schema.read(R2RMLMappingFactory.class.getResourceAsStream("/r2rml.rdf"), null);
+		
+		//Model schema = FileManager.get().loadModel("./resources/r2rml.rdf");
+		InfModel mappingmodel = ModelFactory.createRDFSModel(schema, data);
+
+		List<Resource> list = mappingmodel.listSubjectsWithProperty(RDF.type, R2RML.TriplesMap).toList();
+		
+		if(list.isEmpty()) {
+			logger.error("R2RML Mapping File has no TriplesMaps.");
+			return null;
+		}
+		
+		for(Resource tm : list) {
+			TriplesMap triplesMap = new TriplesMap(tm, baseIRI);
+			if(!triplesMap.preProcessAndValidate()) {
+				// Something went wrong, abort.
+				return null;
+			}
+			mapping.addTriplesMap(tm, triplesMap);
+		}		
+		
+		return mapping;
+	}
+
+
+}

+ 22 - 0
src/r2rml/model/R2RMLResource.java

@@ -0,0 +1,22 @@
+package r2rml.model;
+
+import org.apache.jena.rdf.model.Resource;
+
+/**
+ * R2RMLResource Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public abstract class R2RMLResource {
+	
+	abstract protected boolean preProcessAndValidate();
+
+	protected Resource description = null;
+	
+	public R2RMLResource(Resource description) {
+		this.description = description;
+	}
+	
+}

+ 59 - 0
src/r2rml/model/R2RMLUtil.java

@@ -0,0 +1,59 @@
+package r2rml.model;
+
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.log4j.Logger;
+
+public class R2RMLUtil {
+
+	private static Logger logger = Logger.getLogger(R2RMLUtil.class.getName());
+	
+	public static boolean isValidLanguageTag(RDFNode node) {
+		if(!node.isLiteral()) return false;
+		// TODO: Implement language tag check
+		return true;
+	}
+	
+	/**
+	 * Utility function to check whether the string denoting a column name is valid
+	 * 
+	 * @param columnName
+	 * @return True if a valid column name
+	 */
+	public static boolean isValidColumnName(String columnName) {
+		// TODO: check the actual value of the column is valid
+		return true;
+	}
+
+	public static String createJointQuery(TriplesMap child, TriplesMap parent, List<Join> joins) {
+		// If the child query and parent query of a referencing object 
+		// map are not identical, then the referencing object map must 
+		// have at least one join condition.
+		
+		String cquery = child.getLogicalTable().generateQuery();
+		String pquery = parent.getLogicalTable().generateQuery();
+		
+		if(!cquery.equals(pquery) && joins.isEmpty()) {
+			logger.error("If the child query and parent query of a referencing object map are not identical, then the referencing object map must have at least one join condition.");
+			return null;
+		}
+		
+		// If the referencing object map has no join condition
+		if(joins.isEmpty())
+			return "SELECT * FROM (" + cquery + ") AS tmp";
+		
+		String query = "SELECT * FROM (" + cquery + ") AS child, ";
+        query += "(" + pquery + ") AS parent WHERE ";
+        
+        for(Join join : joins) {
+        	query += "child." + join.getChild() + "=";
+        	query += "parent." + join.getParent() + " AND ";
+        }
+        
+        query += "TRUE";
+        
+        return query;
+	}
+	
+}

+ 81 - 0
src/r2rml/model/RefObjectMap.java

@@ -0,0 +1,81 @@
+package r2rml.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.R2RML;
+
+/**
+ * RefObjectMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class RefObjectMap extends R2RMLResource {
+	
+	private static Logger logger = Logger.getLogger(RefObjectMap.class.getName());
+
+	private List<Join> joins = new ArrayList<Join>();
+	
+	// We keep track of the parent via a resource, as it will be processed
+	// by the engine.
+	private Resource parentTriplesMap = null;
+	
+	public RefObjectMap(Resource description) {
+		super(description);
+	}
+	
+	public List<Join> getJoins() {
+		return joins;
+	}
+	
+	public Resource getParentTriplesMap() {
+		return parentTriplesMap;
+	}
+	
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing RefObjectMap " + description);
+		
+		// Having an rr:parentTriplesMap property
+		List<Statement> list = description.listProperties(R2RML.parentTriplesMap).toList();
+		if(list.size() != 1) {
+			logger.error("RefObjectMap must have exactly one rr:parentTriplesMap.");
+			logger.error(description);
+			return false;
+		}
+		
+		RDFNode node = list.get(0).getObject();
+		if(!node.isResource()) {
+			logger.error("rr:parentTriplesMap must be a resource.");
+			logger.error(description);
+			return false;
+		}
+		
+		parentTriplesMap = node.asResource();
+		
+		// process the join conditions
+		list = description.listProperties(R2RML.joinCondition).toList();
+		for(Statement s : list) {
+			node = s.getObject();
+			if(!node.isResource()) {
+				logger.error("rr:joinCondition must be a resource.");
+				logger.error(description);
+				return false;
+			}
+			Join join = new Join(node.asResource());
+			if(!join.preProcessAndValidate())
+				return false;
+			joins.add(join);
+		}
+		
+		return true;
+	}
+	
+}

+ 113 - 0
src/r2rml/model/SubjectMap.java

@@ -0,0 +1,113 @@
+package r2rml.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.database.Row;
+import r2rml.engine.R2RML;
+import r2rml.engine.R2RMLException;
+
+/**
+ * SubjectMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class SubjectMap extends TermMap {
+	
+	private static Logger logger = Logger.getLogger(SubjectMap.class.getName());
+
+	private List<GraphMap> graphMaps = new ArrayList<GraphMap>();
+	private List<Resource> classes = new ArrayList<Resource>();
+
+	public SubjectMap(Resource description, String baseIRI) {
+		super(description, baseIRI);
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		if(!super.preProcessAndValidate())
+			return false;
+		
+		logger.info("Processing SubjectMap " + description);
+		
+		// Minimum conditions
+		// Being an rr:TermMap -- covered by inference
+		// Being value of an rr:subjectMap property
+		// TODO: Check "Being value of an rr:subjectMap property" in engine
+		
+		List<Statement> list = description.listProperties(R2RML.clazz).toList();
+		for(Statement s : list) {
+			if(!s.getObject().isURIResource()) {
+				logger.error("Value of rr:class must be an IRI.");
+				logger.error(description);
+				return false;
+			}
+			classes.add(s.getObject().asResource());
+		}
+		
+		// Pre-process and validate each GraphMap
+		List<Statement> listg = description.listProperties(R2RML.graphMap).toList();
+		for(Statement s : listg) {
+			if(!s.getObject().isResource()) {
+				logger.error("rr:graphMap must refer to a resource.");
+				logger.error(description);
+				return false;
+			}
+			GraphMap gm = new GraphMap(s.getObject().asResource(), baseIRI);
+			if(!gm.preProcessAndValidate())
+				return false;
+			graphMaps.add(gm);
+		}
+		
+		return true;
+	}
+
+	@Override
+	protected RDFNode distillConstant(RDFNode node) {
+		// If the constant-valued term map is a subject map, predicate map or graph map, 
+		// then its constant value must be an IRI.
+		if(isConstantValuedTermMap()) {
+			if(!node.isURIResource()) {
+				logger.error("Constant for SubjectMap must be an IRI.");
+				logger.error(description);
+				return null;
+			}
+		}
+		return node;
+	}
+
+	@Override
+	protected boolean isChosenTermTypeValid() {
+		if(!(isTermTypeIRI() || isTermTypeBlankNode())) {
+			logger.error("TermType for SubjectMap must be rr:IRI or rr:BlankNode.");
+			logger.error(description);
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	protected Resource inferTermType() {
+		return R2RML.IRI;
+	}
+
+	public List<GraphMap> getGraphMaps() {
+		return graphMaps;
+	}
+
+	public List<Resource> getClasses() {
+		return classes;
+	}
+
+	public Resource generateRDFTerm(Row row) throws R2RMLException {
+		return super.generateRDFTerm(row).asResource();
+	}
+	
+}

+ 336 - 0
src/r2rml/model/TermMap.java

@@ -0,0 +1,336 @@
+package r2rml.model;
+
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.iri.IRI;
+import org.apache.jena.iri.IRIFactory;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.jena.vocabulary.XSD;
+import org.apache.log4j.Logger;
+
+import r2rml.database.Row;
+import r2rml.engine.R2RML;
+import r2rml.engine.R2RMLException;
+import r2rml.engine.R2RMLTypeMapper;
+
+/**
+ * TermMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public abstract class TermMap extends R2RMLResource {
+
+	private static Logger logger = Logger.getLogger(TermMap.class.getName());
+	private Resource termType = null;
+
+	/* 
+	 * Term generation rules for blank nodes. If the term type is rr:BlankNode:
+	 * Return a blank node that is unique to the natural RDF lexical form 
+	 * corresponding to value. This seems to imply that there is a one-on-one
+	 * mapping for each value and blank node. We will thus map the outcome
+	 * of the constant, template, or column to the same blank node. In other
+	 * words, if two TermMaps use "test"^^xsd:string, return same blank node.
+	 * "1"^^xsd:string and "1"^^xsd:integer are different.
+	 * 
+	 */
+	private static Map<Object, Resource> blankNodeMap = new HashMap<Object, Resource>();
+
+	private String template;
+	private RDFNode constant;
+	private String column;
+
+	protected String language = null;
+	protected Resource datatype = null;
+	protected String baseIRI = null;
+
+	public TermMap(Resource description, String baseIRI) {
+		super(description);
+		this.baseIRI = baseIRI;
+	}
+
+	@Override
+	protected boolean preProcessAndValidate() {
+		logger.info("Processing TermMap " + description);
+
+		List<Statement> templates = description.listProperties(R2RML.template).toList();
+		List<Statement> constants = description.listProperties(R2RML.constant).toList();
+		List<Statement> columns = description.listProperties(R2RML.column).toList();
+
+		// Having exactly one of rr:constant, rr:column, rr:template
+		if(templates.size() + constants.size() + columns.size() != 1) {
+			logger.error("TermMap must have exactly one of rr:constant, rr:column, and rr:template.");
+			logger.error(description);
+			return false;
+		}
+
+		// The value of the rr:column property must be a valid column name.
+		if(columns.size() == 1) {
+			column = distillColumnName(columns.get(0).getObject());
+			if(column == null) {
+				logger.error("The value of the rr:column property must be a valid column name.");
+				logger.error(description);
+				return false;
+			}
+		} else if(templates.size() == 1) {
+			// Check whether it is a valid template
+			template = distillTemplate(templates.get(0).getObject());
+			if(template == null) {
+				logger.error("The value of the rr:template property must be a valid string template.");
+				logger.error(description);
+				return false;
+			}
+
+			// Check whether the referenced column names are valid
+			for(String columnName : getReferencedColumns()) {
+				if(!R2RMLUtil.isValidColumnName(columnName)) {
+					logger.error("Invalid column name in rr:template: " + columnName);
+					logger.error(description);
+					return false;
+				}
+			}
+		} else if(constants.size() == 1) {
+			// the check for ConstantValuedTermMaps are local (different rules
+			// for different TermMaps.
+			constant = distillConstant(constants.get(0).getObject());
+			if(constant == null)
+				return false;
+		}
+
+		// Validity of the termType is also local. 
+		// At most one and compute default one if absent.
+		List<Statement> list = description.listProperties(R2RML.termType).toList();
+		if(list.size() > 1) {
+			logger.error("TermMap can have at most one rr:termType.");
+			logger.error(description);
+			return false;
+		} else if (list.size() == 0) {
+			termType = inferTermType();
+		} else {
+			// We have exactly one value. Check validity.
+			// Is it a valid IRI?
+			if(!list.get(0).getObject().isURIResource()) {
+				logger.error("TermMap's rr:termType must be a valid IRI.");
+				logger.error(description);
+				return false;
+			}
+
+			termType = list.get(0).getObject().asResource();
+			// Is it a valid option?
+			if(!isChosenTermTypeValid())
+				return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Infer "default" termtype.
+	 * @return
+	 */
+	protected abstract Resource inferTermType();
+
+	/**
+	 * True if chosen TermType is valid for subclasses.
+	 * @return 
+	 */
+	protected abstract boolean isChosenTermTypeValid();
+
+	/**
+	 * True if the conditions for constant values for one of the TermMap's subclasses
+	 * are met.
+	 * @return 
+	 */
+	protected abstract RDFNode distillConstant(RDFNode node);
+
+	private String distillTemplate(RDFNode node) {
+		if(!node.isLiteral()) 
+			return null;
+		if(!node.asLiteral().getDatatype().getURI().equals(XSD.xstring.getURI()))
+			return null;
+		// TODO: check the actual value of the template
+		return node.asLiteral().toString();
+	}
+
+	private Set<String> getReferencedColumns() {
+		Set<String> set = new HashSet<String>();
+		if(isColumnValuedTermMap()) {
+			// Singleton
+			set.add(column);
+		} else if(isTemplateValuedTermMap()) {
+			// Little hack to "ignore" escaped curly braces.
+			String temp = template.replace("\\{", "--").replace("\\}", "--");
+			Matcher m = Pattern.compile("\\{([^}]+)\\}").matcher(temp);
+			while(m.find()) {
+				set.add(template.substring(m.start(1), m.end(1)));
+			}
+		} // else constant and thus empty set.
+		return set;
+	}
+
+	private String distillColumnName(RDFNode node) {
+		if(!node.isLiteral()) 
+			return null;
+		if(!node.asLiteral().getDatatype().getURI().equals(XSD.xstring.getURI()))
+			return null;
+		String s = node.asLiteral().toString();
+		if(!R2RMLUtil.isValidColumnName(s))
+			return null;
+		return s;
+	}
+
+	public boolean isTemplateValuedTermMap() {
+		return template != null;
+	}
+
+	public boolean isColumnValuedTermMap() {
+		return column != null;
+	}
+
+	public boolean isConstantValuedTermMap() {
+		return constant != null;
+	}
+
+	public Resource getTermType() {
+		return termType;
+	}
+
+	public boolean isTermTypeBlankNode() {
+		return getTermType().getURI().equals(R2RML.BLANKNODE.getURI());
+	}
+
+	public boolean isTermTypeIRI() {
+		return getTermType().getURI().equals(R2RML.IRI.getURI());
+	}
+
+	public boolean isTermTypeLiteral() {
+		return getTermType().getURI().equals(R2RML.LITERAL.getURI());
+	}
+
+	public RDFNode generateRDFTerm(Row row) throws R2RMLException {
+		Object value = getValueForRDFTerm(row);
+		// If value is NULL, then no RDF term is generated.
+		if(value == null) {
+			return null;
+		}
+
+		else if(isTermTypeIRI()) {
+			/* Otherwise, if the term map's term type is rr:IRI: 1. Let value 
+			 * be the natural RDF lexical form corresponding to value. 2. If 
+			 * value is a valid absolute IRI [RFC3987], then return an IRI 
+			 * generated from value. 3. Otherwise, prepend value with the base
+			 * IRI. If the result is a valid absolute IRI [RFC3987], then 
+			 * return an IRI generated from the result. 4. Otherwise, raise a 
+			 * data error.
+			 */
+			IRI iri = IRIFactory.iriImplementation().create(value.toString());
+			if(iri.isAbsolute())
+				return ResourceFactory.createResource(convertToIRISafeVersion(iri));
+			
+			iri = IRIFactory.iriImplementation().create(baseIRI + value);
+			if(iri.isAbsolute())
+				return ResourceFactory.createResource(convertToIRISafeVersion(iri));
+			
+			throw new R2RMLException("Data error. " + baseIRI + value + " is not a valid absolute IRI", null);
+			
+		}
+		/*
+		 * Otherwise, if the term type is rr:BlankNode: Return a blank node 
+		 * that is unique to the natural RDF lexical form corresponding to 
+		 * value. 
+		 */
+		else if(isTermTypeBlankNode()) {
+			Resource r = blankNodeMap.get(value);
+			if(r == null) {
+				r = ResourceFactory.createResource();
+				blankNodeMap.put(value, r);
+			}
+			return r;
+		}
+		/*
+		 * Otherwise, if the term type is rr:Literal:
+		 * 1. If the term map has a specified language tag, then return a plain
+		 *    literal with that language tag and with the natural RDF lexical 
+		 *    form corresponding to value.
+		 * 2. Otherwise, if the term map has a non-empty specified datatype 
+		 *    that is different from the natural RDF datatype corresponding to 
+		 *    the term map's implicit SQL datatype, then return the datatype-
+		 *    override RDF literal corresponding to value and the specified 
+		 *    datatype.
+		 *    Otherwise, return the natural RDF literal corresponding to value.
+		 *    
+		 *    // TODO: we use Jena's converter...
+		 */
+		else if(isTermTypeLiteral()) {
+			if(language != null) {
+				return ResourceFactory.createLangLiteral(value.toString(), language);
+			}
+			if(datatype != null) {
+				RDFDatatype d = R2RMLTypeMapper.getTypeByName(datatype);
+				return ResourceFactory.createTypedLiteral(value.toString(), d);
+			}
+			return ResourceFactory.createTypedLiteral(value);
+		}
+		return null;
+	}
+
+	/**
+	 * Private method to create safe IRIs. It does percent encoding. According
+	 * to the R2RML Standard, however, some characters (like kanji) should not
+	 * be percent encoded. This is what jena does. We need to find a library
+	 * that better complies with the standard.
+	 * 
+	 * 42					-> 42						OK
+	 * Hello World!			-> Hello%20World%21 		OK
+	 * 2011-08-23T22:17:00Z	-> 2011-08-23T22%3A17%3A00Z	OK
+	 * ~A_17.1-2			-> ~A_17.1-2				OK
+	 * 葉篤正					-> 葉篤正						NOK!
+	 * 
+	 * TODO: Better complient safe IRI conversion.
+	 * 
+	 * @param iri
+	 * @return
+	 * @throws R2RMLException
+	 */
+	private String convertToIRISafeVersion(IRI iri) throws R2RMLException {
+		try {
+			return iri.toASCIIString();
+		} catch (MalformedURLException e) {
+			throw new R2RMLException("Problem generating safe IRI " + iri, e);
+		}
+	}
+
+	private Object getValueForRDFTerm(Row row) throws R2RMLException {
+		if(isConstantValuedTermMap()) {
+			return constant;
+		} else if(isColumnValuedTermMap()) {
+			return row.getObject(column);
+		} else if(isTemplateValuedTermMap()) {
+			String value = new String(template);
+			for(String reference : getReferencedColumns()) {
+				String s = row.getObject(reference).toString();
+				// first argument is a regular expression, therefore we
+				// have to escape the curly braces, but in a string that
+				// also means escaping the escape character.
+				value = value.replaceAll("\\{" + reference + "\\}", s);
+			}
+			// Unescape all the values!
+			value = StringEscapeUtils.unescapeJava(value);
+			return value;
+		}
+		return null;
+	}
+}

+ 346 - 0
src/r2rml/model/TriplesMap.java

@@ -0,0 +1,346 @@
+package r2rml.model;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jena.query.Dataset;
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.log4j.Logger;
+
+import r2rml.database.DB;
+import r2rml.database.Row;
+import r2rml.database.Rows;
+import r2rml.engine.R2RML;
+import r2rml.engine.R2RMLException;
+
+/**
+ * TriplesMap Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class TriplesMap extends R2RMLResource {
+
+	private static Logger logger = Logger.getLogger(TriplesMap.class.getName());
+
+	private LogicalTable logicalTable = null;
+	private SubjectMap subjectMap = null;
+	private List<PredicateObjectMap> predicateObjectMaps = new ArrayList<PredicateObjectMap> ();
+	private String baseIRI = null;
+
+	private int count = 0;
+
+	public TriplesMap(Resource description, String baseIRI) {
+		super(description);
+		this.setBaseIRI(baseIRI);
+	}
+
+	public LogicalTable getLogicalTable() {
+		return logicalTable;
+	}
+
+	public void setLogicalTable(LogicalTable logicalTable) {
+		this.logicalTable = logicalTable;
+	}
+
+	public SubjectMap getSubjectMap() {
+		return subjectMap;
+	}
+
+	public void setSubjectMap(SubjectMap subjectMap) {
+		this.subjectMap = subjectMap;
+	}
+
+	public List<PredicateObjectMap> getPredicateObjectMaps() {
+		return predicateObjectMaps;
+	}
+
+	public void setPredicateObjectMaps(List<PredicateObjectMap> predicateObjectMaps) {
+		this.predicateObjectMaps = predicateObjectMaps;
+	}
+
+	@Override
+	public boolean preProcessAndValidate() {
+		logger.info("Processing TriplesMap " + description);
+
+		// TermMap must have an rr:logicalTable property (exactly one?)
+		List<Statement> list = description.listProperties(R2RML.logicalTable).toList();
+		if(list.size() != 1) {
+			logger.error("TriplesMap must have exactly one rr:logicalTable property.");
+			logger.error(description);
+			return false;
+		}
+
+		RDFNode node = list.get(0).getObject();
+		if(!node.isResource()) {
+			logger.error("LogicalTable of TriplesMap is not a resource.");
+			logger.error(description);
+			return false;
+		}
+
+		// Pre-process and validate LogicalTable
+		logicalTable = new LogicalTable(node.asResource());
+		if(!logicalTable.preProcessAndValidate())
+			return false;
+
+		// TermMap must have exactly one of rr:subject and rr:subjectMap
+		// But we constructed rr:subjectMap from rr:subject, thus only check one!
+		list = description.listProperties(R2RML.subjectMap).toList();		
+		if(list.size() != 1) {
+			logger.error("TriplesMap must have exactly one one of rr:subject and rr:subjectMap.");
+			logger.error(description);
+			return false;
+		}
+
+		node = list.get(0).getObject();
+		if(!node.isResource()) {
+			logger.error("SubjectMap of TriplesMap is not a resource.");
+			logger.error(description);
+			return false;
+		}
+
+		// Pre-process and validate SubjectMap
+		subjectMap = new SubjectMap(node.asResource(), baseIRI);
+		if(!subjectMap.preProcessAndValidate())
+			return false;
+
+		// Pre-process and validate PredicateObjectMaps
+		// TriplesMaps may have zero or more PredicateObjectMaps
+		// Just iterate over them.
+		list = description.listProperties(R2RML.predicateObjectMap).toList();
+		for(Statement s : list) {
+			node = s.getObject();
+			if(!node.isResource()) {
+				logger.error("PredicateObjectMap is not a resource.");
+				logger.error(description);
+				return false;
+			}
+
+			PredicateObjectMap opm = new PredicateObjectMap(s.getObject().asResource(), baseIRI);
+			if(!opm.preProcessAndValidate())
+				return false;
+			predicateObjectMaps.add(opm);
+		}
+
+		return true;
+	}
+
+	public boolean generateTriples(
+			DB database, 
+			Dataset dataset, 
+			Map<Resource, TriplesMap> triplesMaps) {
+		
+		try {
+			String query = getLogicalTable().generateQuery();
+			Rows rows = database.getRows(query);
+
+			List<Resource> classes = getSubjectMap().getClasses();
+			List<GraphMap> sgm = getSubjectMap().getGraphMaps();
+
+			logger.info("TriplesMap " + description + ": start generating triples.");
+
+			int child_column_count = rows.getColumnCount();
+
+			Row row = null;
+			while((row = rows.nextRow()) != null) {
+				Resource subject = getSubjectMap().generateRDFTerm(row);
+
+				// According to term generation rules, if the value is null,
+				// then no RDFTerm is generated. If that is the case for the
+				// subject, then we do not generate RDF for that resource.
+				if(subject != null) {
+
+					Set<String> subjectGraphs = new HashSet<String>();
+					for(GraphMap gm : sgm) {
+						Resource gmiri = gm.generateRDFTerm(row);
+						if(gmiri != null)
+							subjectGraphs.add(gmiri.getURI());
+					}
+
+					for(Resource c : classes) {
+						addTriplesToDataset(dataset, subject, RDF.type, c, subjectGraphs);
+					}
+
+					// Now process each predicate-object map of the triples map
+					for(PredicateObjectMap opm : getPredicateObjectMaps()) {
+						// Let predicates be the set of generated RDF terms that 
+						// result from applying each of the predicate-object 
+						// map's predicate maps to row
+						List<Property> predicates = new ArrayList<Property>();
+						for(PredicateMap pm : opm.getPredicateMaps()) {
+							Property p = pm.generateRDFTerm(row);
+							if(p != null)
+								predicates.add(p);
+						}
+
+						// Let objects be the set of generated RDF terms that result 
+						// from applying each of the predicate-object map's object 
+						// maps (but not referencing object maps) to row
+						List<RDFNode> objects = new ArrayList<RDFNode>();
+						for(ObjectMap om : opm.getObjectMaps()) {
+							RDFNode o = om.generateRDFTerm(row); 
+							if(o != null)
+								objects.add(o);
+						}
+
+						// Let pogm be the set of graph maps of the predicate-object 
+						// map. Let predicate-object_graphs be the set of generated 
+						// RDF terms that result from applying each graph map in pogm 
+						// to row
+						Set<String> pogs = new HashSet<String>();
+						for(GraphMap gm : opm.getGraphMaps()) {
+							Resource gmiri = gm.generateRDFTerm(row);
+							if(gmiri != null)
+								pogs.add(gmiri.getURI());
+						}
+
+						// Target graphs: If sgm and pogm are empty: rr:defaultGraph; 
+						// otherwise: union of subject_graphs and predicate-object_graphs
+						pogs.addAll(subjectGraphs);
+						for(Property p : predicates) {
+							for(RDFNode o : objects) {
+								addTriplesToDataset(dataset, subject, p, o, pogs);
+							}
+						}
+
+
+					}
+				}
+			} // end while
+
+			// For each referencing object map of a predicate-object map of 
+			// the triples map, apply the following steps:
+			for(PredicateObjectMap opm : getPredicateObjectMaps()) {
+				for(RefObjectMap rof : opm.getRefObjectMaps()) {
+					TriplesMap ptm = triplesMaps.get(rof.getParentTriplesMap());
+					SubjectMap psm = ptm.getSubjectMap();
+					List<GraphMap> pogm = opm.getGraphMaps();
+					
+					String jointQuery = R2RMLUtil.createJointQuery(this, ptm, rof.getJoins());
+					if(jointQuery == null)
+						return false;
+
+					Rows rows2 = database.getRows(jointQuery);
+					int column_count = rows2.getColumnCount();
+					// For each row in rows2...
+					while(rows2.nextRow() != null) {
+						Row child_row = rows2.projectCurrentRow(1, child_column_count);
+						Row parent_row = rows2.projectCurrentRow(child_column_count + 1, column_count);
+
+						// Let subject be the generated RDF term that results 
+						// from applying sm to "child_row"
+						Resource subject = getSubjectMap().generateRDFTerm(child_row);
+
+						// Let object be the generated RDF term that results 
+						// from applying psm to parent_row
+						Resource object = psm.generateRDFTerm(parent_row);
+						
+						// if subject or object is NULL, don't generate triples
+						if(subject != null || object != null) {
+
+							// Let predicates be the set of generated RDF terms that result 
+							// from applying each of the predicate-object map's predicate 
+							// maps to child_row
+							List<Property> predicates = new ArrayList<Property>();
+							for(PredicateMap pm : opm.getPredicateMaps()) {
+								Property p = pm.generateRDFTerm(child_row);
+								if(p != null)
+									predicates.add(p);
+							}
+
+							// Let subject_graphs be the set of generated RDF terms 
+							// that result from applying each graph map of sgm to 
+							// child_row
+							Set<String> subjectGraphs = new HashSet<String>();
+							for(GraphMap gm : sgm) {
+								Resource gmiri = gm.generateRDFTerm(child_row);
+								if(gmiri != null)
+									subjectGraphs.add(gmiri.getURI());
+							}
+
+							// Let predicate-object_graphs be the set of generated 
+							// RDF terms that result from applying each graph map in 
+							// pogm to child_row
+							Set<String> pogs = new HashSet<String>();
+							for(GraphMap gm : pogm) {
+								Resource gmiri = gm.generateRDFTerm(child_row);
+								if(gmiri != null)
+									pogs.add(gmiri.getURI());
+							}
+
+							// Target graphs: If sgm and pogm are empty: rr:defaultGraph; 
+							// otherwise: union of subject_graphs and predicate-object_graphs
+							pogs.addAll(subjectGraphs);
+							for(Property p : predicates) {
+								addTriplesToDataset(dataset, subject, p, object, pogs);
+							}
+						}
+					}
+				}
+			}
+
+			logger.info("TriplesMap " + description + ": generated triples = " + count);
+
+		} catch (R2RMLException e) {
+			logger.error("R2RMLException.", e);
+			logger.error(description);
+			return false;
+		} finally {
+			if(database != null) {
+				try {
+					database.close();
+				} catch (R2RMLException e) {
+					logger.error("R2RMLException.", e);
+					logger.error(description);
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Adding triples to the dataset. If the set of named graphs is empty, they
+	 * added to the default model.
+	 * 
+	 * @param ds The dataset
+	 * @param s The subject
+	 * @param p The predicate
+	 * @param o The object
+	 * @param ngs The set of named graphs
+	 */
+	private void addTriplesToDataset(Dataset ds, Resource s, Property p, RDFNode o, Set<String> ngs) {
+		if(ngs.isEmpty()) {
+			ds.getDefaultModel().add(s, p, o);
+			count++;
+			return;
+		}
+
+		for(String ng : ngs) {
+			if(ng.equals(R2RML.defaultGraph.getURI())) {
+				ds.getDefaultModel().add(s, p, o);
+				count++;
+			} else {
+				ds.getNamedModel(ng).add(s, p, o);
+				count++;
+			}
+		}
+	}
+
+	public String getBaseIRI() {
+		return baseIRI;
+	}
+
+	public void setBaseIRI(String baseIRI) {
+		this.baseIRI = baseIRI;
+	}
+
+}

+ 16 - 0
test/resources/01.mapping.ttl

@@ -0,0 +1,16 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+        rr:class ex:Employee;
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "ENAME" ];
+    ].
+	
+<#a> rr:objectMap <#b> .
+<#b> rr:column "ENAME" .

+ 4 - 0
test/resources/01.output.ttl

@@ -0,0 +1,4 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.
+<http://data.example.com/employee/7369> ex:name "SMITH".

+ 29 - 0
test/resources/02.mapping.ttl

@@ -0,0 +1,29 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#DeptTableView> rr:sqlQuery """
+SELECT DEPTNO,
+       DNAME,
+       LOC,
+       (SELECT COUNT(*) FROM EMP WHERE EMP.DEPTNO=DEPT.DEPTNO) AS STAFF
+FROM DEPT
+""".
+
+<#TriplesMap2>
+    rr:logicalTable <#DeptTableView>;
+    rr:subjectMap [
+        rr:template "http://data.example.com/department/{DEPTNO}";
+        rr:class ex:Department;
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "DNAME" ];
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:location;
+        rr:objectMap [ rr:column "LOC" ];
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:staff;
+        rr:objectMap [ rr:column "STAFF" ];
+    ].

+ 6 - 0
test/resources/02.output.ttl

@@ -0,0 +1,6 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/department/10> rdf:type ex:Department.
+<http://data.example.com/department/10> ex:name "APPSERVER".
+<http://data.example.com/department/10> ex:location "NEW YORK".
+<http://data.example.com/department/10> ex:staff 1.

+ 32 - 0
test/resources/03.mapping.ttl

@@ -0,0 +1,32 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+    ];
+	rr:predicateObjectMap [
+		rr:predicate ex:department;
+		rr:objectMap [
+			rr:parentTriplesMap <#TriplesMap2>;
+			rr:joinCondition [
+				rr:child "DEPTNO";
+				rr:parent "DEPTNO";
+			];
+		];
+	].
+	
+<#TriplesMap2>
+    rr:logicalTable <#DeptTableView>;
+    rr:subjectMap [
+        rr:template "http://data.example.com/department/{DEPTNO}";
+    ].
+	
+<#DeptTableView> rr:sqlQuery """
+	SELECT DEPTNO,
+	       DNAME,
+	       LOC,
+	       (SELECT COUNT(*) FROM EMP WHERE EMP.DEPTNO=DEPT.DEPTNO) AS STAFF
+	FROM DEPT
+	""".

+ 3 - 0
test/resources/03.output.ttl

@@ -0,0 +1,3 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> ex:department <http://data.example.com/department/10> .

+ 15 - 0
test/resources/04.mapping.ttl

@@ -0,0 +1,15 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap3>
+    rr:logicalTable [ rr:tableName "EMP2DEPT" ];
+    rr:subjectMap [ rr:template "http://data.example.com/employee={EMPNO}/department={DEPTNO}" ];
+    rr:predicateObjectMap [
+        rr:predicate ex:employee;
+        rr:objectMap [ rr:template "http://data.example.com/employee/{EMPNO}" ];
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:department;
+        rr:objectMap [ rr:template "http://data.example.com/department/{DEPTNO}" ];
+    ].
+    

+ 14 - 0
test/resources/04.output.ttl

@@ -0,0 +1,14 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+
+<http://data.example.com/employee=7369/department=10> 
+    ex:employee   <http://data.example.com/employee/7369> ;
+    ex:department <http://data.example.com/department/10> .
+
+<http://data.example.com/employee=7369/department=20> 
+    ex:employee <http://data.example.com/employee/7369> ;
+    ex:department <http://data.example.com/department/20> .
+
+<http://data.example.com/employee=7400/department=10> 
+    ex:employee <http://data.example.com/employee/7400> ;
+    ex:department <http://data.example.com/department/10> .

+ 12 - 0
test/resources/05.mapping.ttl

@@ -0,0 +1,12 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap3>
+    rr:logicalTable [ rr:tableName "EMP2DEPT" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+    ];
+    rr:predicateObjectMap [
+      rr:predicate ex:department;
+      rr:objectMap [ rr:template "http://data.example.com/department/{DEPTNO}" ];
+    ].

+ 9 - 0
test/resources/05.output.ttl

@@ -0,0 +1,9 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> 
+    ex:department <http://data.example.com/department/10> ;
+    ex:department <http://data.example.com/department/20> .
+
+<http://data.example.com/employee/7400> 
+    ex:department <http://data.example.com/department/10>.
+    

+ 20 - 0
test/resources/06.mapping.ttl

@@ -0,0 +1,20 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:sqlQuery """
+
+        SELECT EMP.*, (CASE JOB
+            WHEN 'CLERK' THEN 'general-office'
+            WHEN 'NIGHTGUARD' THEN 'security'
+            WHEN 'ENGINEER' THEN 'engineering'
+        END) ROLE FROM EMP
+
+        """ ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:role;
+        rr:objectMap [ rr:template "http://data.example.com/roles/{ROLE}" ];
+    ].

+ 5 - 0
test/resources/06.output.ttl

@@ -0,0 +1,5 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> ex:role <http://data.example.com/roles/general-office>.
+
+    

+ 10 - 0
test/resources/07.mapping.ttl

@@ -0,0 +1,10 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ] ;
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}" ;
+		rr:class ex:Employee ;
+    ] ;
+	.

+ 4 - 0
test/resources/07.output.ttl

@@ -0,0 +1,4 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.
+    

+ 14 - 0
test/resources/08.mapping.ttl

@@ -0,0 +1,14 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+    ];
+	rr:predicateObjectMap [
+		rr:predicateMap [ rr:constant rdf:type ];
+		rr:objectMap [ rr:constant ex:Employee ];
+	].
+	

+ 3 - 0
test/resources/08.output.ttl

@@ -0,0 +1,3 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.

+ 15 - 0
test/resources/09.mapping.ttl

@@ -0,0 +1,15 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+    ];
+    rr:predicateObjectMap [
+		rr:predicate rdf:type ;
+		rr:object ex:Employee ;
+    ]
+	.
+	

+ 3 - 0
test/resources/09.output.ttl

@@ -0,0 +1,3 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.

+ 19 - 0
test/resources/10.mapping.ttl

@@ -0,0 +1,19 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "DEPT" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/site/{LOC}";
+		rr:class ex:Site;
+    ];
+	rr:predicateObjectMap [
+        rr:predicate ex:test;
+        rr:objectMap [ 
+			rr:template "\\{\\{\\{ \\\\o/ {LOC} \\\\o/ \\}\\}\\}"; 
+			rr:termType rr:Literal;
+		];
+    ]
+	.
+	

+ 7 - 0
test/resources/10.output.rdf

@@ -0,0 +1,7 @@
+<rdf:RDF
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+    xmlns:j.0="http://example.com/ns#">
+  <j.0:Site rdf:about="http://data.example.com/site/NEW%20YORK">
+    <j.0:test>{{{ \o/ NEW YORK \o/ }}}</j.0:test>
+  </j.0:Site>
+</rdf:RDF>

+ 7 - 0
test/resources/11.config.properties

@@ -0,0 +1,7 @@
+connectionURL = jdbc:derby:memory:testing
+user = christophe
+password = secret
+mappingFile = mapping.ttl
+outputFile = output.ttl
+format = TURTLE
+baseIRI = http://www.example.org/

+ 4 - 0
test/resources/12.compareoutput.ttl

@@ -0,0 +1,4 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.
+<http://data.example.com/employee/7369> ex:name "SMITH".

+ 4 - 0
test/resources/12.config.properties

@@ -0,0 +1,4 @@
+connectionURL = jdbc:derby:memory:testing
+mappingFile = ./test/resources/12.mapping.ttl
+outputFile = ./test/resources/12.output.ttl
+format = TURTLE

+ 16 - 0
test/resources/12.mapping.ttl

@@ -0,0 +1,16 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+        rr:class ex:Employee;
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "ENAME" ];
+    ].
+	
+<#a> rr:objectMap <#b> .
+<#b> rr:column "ENAME" .

+ 3 - 0
test/resources/12.output.ttl

@@ -0,0 +1,3 @@
+<http://data.example.com/employee/7369>
+        a                             <http://example.com/ns#Employee> ;
+        <http://example.com/ns#name>  "SMITH" .

+ 4 - 0
test/resources/13.config.properties

@@ -0,0 +1,4 @@
+connectionURL = jdbc:derby:memory:testing
+mappingFile = ./test/resources/13.mapping.ttl
+outputFile = ./test/resources/13.output
+filePerGraph = true

+ 17 - 0
test/resources/13.mapping.ttl

@@ -0,0 +1,17 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+        rr:class ex:Employee;
+    ];
+    rr:predicateObjectMap [
+		rr:graph <http://foo.bar/bar/>;
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "ENAME" ];
+    ].
+	
+<#a> rr:objectMap <#b> .
+<#b> rr:column "ENAME" .

+ 2 - 0
test/resources/13.output/default.ttl

@@ -0,0 +1,2 @@
+<http://data.example.com/employee/7369>
+        a       <http://example.com/ns#Employee> .

+ 2 - 0
test/resources/13.output/http_foo_bar_bar_.ttl

@@ -0,0 +1,2 @@
+<http://data.example.com/employee/7369>
+        <http://example.com/ns#name>  "SMITH" .

+ 141 - 0
test/resources/mysql.sql

@@ -0,0 +1,141 @@
+-- phpMyAdmin SQL Dump
+-- version 4.2.10
+-- http://www.phpmyadmin.net
+--
+-- Host: localhost:3306
+-- Generation Time: May 10, 2016 at 08:47 PM
+-- Server version: 5.5.38
+-- PHP Version: 5.6.2
+
+SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
+SET time_zone = "+00:00";
+
+--
+-- Database: `r2rml`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `DEPT`
+--
+
+CREATE TABLE `DEPT` (
+  `DEPTNO` int(11) NOT NULL,
+  `DNAME` varchar(30) DEFAULT NULL,
+  `LOC` varchar(30) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `DEPT`
+--
+
+INSERT INTO `DEPT` (`DEPTNO`, `DNAME`, `LOC`) VALUES
+(10, 'APPSERVER', 'NEW YORK');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `DEPT2`
+--
+
+CREATE TABLE `DEPT2` (
+  `DEPTNO` int(11) DEFAULT NULL,
+  `DNAME` varchar(30) DEFAULT NULL,
+  `LOC` varchar(30) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `DEPT2`
+--
+
+INSERT INTO `DEPT2` (`DEPTNO`, `DNAME`, `LOC`) VALUES
+(10, 'APPSERVER', 'NEW YORK'),
+(20, 'RESEARCH', 'BOSTON');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `EMP`
+--
+
+CREATE TABLE `EMP` (
+  `EMPNO` int(11) NOT NULL,
+  `ENAME` varchar(100) DEFAULT NULL,
+  `JOB` varchar(20) DEFAULT NULL,
+  `DEPTNO` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `EMP`
+--
+
+INSERT INTO `EMP` (`EMPNO`, `ENAME`, `JOB`, `DEPTNO`) VALUES
+(7369, 'SMITH', 'CLERK', 10);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `EMP2`
+--
+
+CREATE TABLE `EMP2` (
+  `EMPNO` int(11) DEFAULT NULL,
+  `ENAME` varchar(100) DEFAULT NULL,
+  `JOB` varchar(20) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `EMP2`
+--
+
+INSERT INTO `EMP2` (`EMPNO`, `ENAME`, `JOB`) VALUES
+(7369, 'SMITH', 'CLERK'),
+(7369, 'SMITH', 'NIGHTGUARD'),
+(7400, 'JONES', 'ENGINEER');
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `EMP2DEPT`
+--
+
+CREATE TABLE `EMP2DEPT` (
+  `EMPNO` int(11) DEFAULT NULL,
+  `DEPTNO` int(11) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `EMP2DEPT`
+--
+
+INSERT INTO `EMP2DEPT` (`EMPNO`, `DEPTNO`) VALUES
+(7369, 10),
+(7369, 20),
+(7400, 10);
+
+--
+-- Indexes for dumped tables
+--
+
+--
+-- Indexes for table `DEPT`
+--
+ALTER TABLE `DEPT`
+ ADD PRIMARY KEY (`DEPTNO`);
+
+--
+-- Indexes for table `EMP`
+--
+ALTER TABLE `EMP`
+ ADD PRIMARY KEY (`EMPNO`), ADD KEY `DEPTNO` (`DEPTNO`);
+
+--
+-- Constraints for dumped tables
+--
+
+--
+-- Constraints for table `EMP`
+--
+ALTER TABLE `EMP`
+ADD CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`DEPTNO`) REFERENCES `DEPT` (`DEPTNO`);

+ 275 - 0
test/test/TestR2RML.java

@@ -0,0 +1,275 @@
+package test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLNonTransientConnectionException;
+import java.sql.Statement;
+
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.junit.BeforeClass;
+
+import junit.framework.TestCase;
+import r2rml.Main;
+import r2rml.engine.Configuration;
+import r2rml.engine.R2RMLException;
+import r2rml.engine.R2RMLProcessor;
+
+/**
+ * Unit test for testing the functionality of this implementation using an
+ * in memory database.
+ * 
+ * @author Christophe Debruyne
+ *
+ */
+public class TestR2RML extends TestCase {
+
+	private static Logger logger = Logger.getLogger(TestR2RML.class.getName());
+	private static String connectionURL = "jdbc:derby:memory:testing";
+
+	public TestR2RML(String testName) {
+		super(testName);
+	}
+
+	@BeforeClass
+	public static void init() throws Exception {
+		// Log4J junit configuration.
+		BasicConfigurator.configure();
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		try {
+			logger.info("Starting in-memory database for unit tests");
+			Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
+			DriverManager.getConnection(connectionURL + ";create=true").close();
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			fail("Exception during database startup.");
+		}
+		try {
+			Connection connection = DriverManager.getConnection(connectionURL);
+			Statement statement = connection.createStatement();
+			statement.execute("CREATE TABLE DEPT(DEPTNO INT PRIMARY KEY, DNAME VARCHAR(30), LOC VARCHAR(30))");
+			statement.execute("CREATE TABLE EMP(EMPNO INT PRIMARY KEY, ENAME VARCHAR(100), JOB VARCHAR(20), DEPTNO INT, FOREIGN KEY (DEPTNO) REFERENCES DEPT(DEPTNO))");
+			statement.execute("INSERT INTO DEPT VALUES (10, 'APPSERVER', 'NEW YORK')");
+			statement.execute("INSERT INTO EMP VALUES (7369, 'SMITH', 'CLERK', 10)");
+
+			statement.execute("CREATE TABLE DEPT2(DEPTNO INT, DNAME VARCHAR(30), LOC VARCHAR(30))");
+			statement.execute("CREATE TABLE EMP2(EMPNO INT, ENAME VARCHAR(100), JOB VARCHAR(20))");
+			statement.execute("CREATE TABLE EMP2DEPT(EMPNO INT, DEPTNO INT)");
+			statement.execute("INSERT INTO DEPT2 VALUES (10, 'APPSERVER', 'NEW YORK')");
+			statement.execute("INSERT INTO DEPT2 VALUES (20, 'RESEARCH', 'BOSTON')");
+			statement.execute("INSERT INTO EMP2 VALUES (7369, 'SMITH', 'CLERK')");
+			statement.execute("INSERT INTO EMP2 VALUES (7369, 'SMITH', 'NIGHTGUARD')");
+			statement.execute("INSERT INTO EMP2 VALUES (7400, 'JONES', 'ENGINEER')");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7369, 10)");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7369, 20)");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7400, 10)");
+			statement.close();
+			connection.close();
+
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			fail("Failure setting up the database.");
+		}
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		logger.info("Stopping in-memory database.");
+		try {
+			DriverManager.getConnection(connectionURL + ";drop=true").close();
+		} catch (SQLNonTransientConnectionException ex) {
+			if (ex.getErrorCode() != 45000) {
+				throw ex;
+			}
+			// Shutdown success
+		}
+	}
+
+	/**
+	 * 2.3 Example: Mapping a Simple Table
+	 * https://www.w3.org/TR/r2rml/#example-simple
+	 */
+	public void testExample01() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/01.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/01.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());	
+	}
+
+	/**
+	 * 2.4 Example: Computing a Property with an R2RML View
+	 */
+	public void testExample02() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/02.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/02.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());	
+	}
+
+	/**
+	 * 2.5 Example: Linking Two Tables
+	 */
+	public void testExample03() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/03.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/03.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	/**
+	 * Many-to-Many 1
+	 */
+	public void testExample04() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/04.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/04.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	/**
+	 * Many-to-Many 2
+	 */
+	public void testExample05() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/05.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/05.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample06() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/06.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/06.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample07() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/07.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/07.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample08() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/08.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/08.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample09() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/09.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/09.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample10() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/10.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		// HAD TO CREATE AN RDF/XML FILE AS JENA IS UNABLE TO PARSE "\o" AS TTL
+		// IT THINKS YOU ARE TRYING TO ESCAPE AN "o"... NO PROBLEM WITH RDF/XML
+		target.read("./test/resources/10.output.rdf");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample11() throws R2RMLException {
+		Configuration configuration = new Configuration("./test/resources/11.config.properties");
+		assertEquals("jdbc:derby:memory:testing", configuration.getConnectionURL());
+		assertEquals("christophe", configuration.getUser());
+		assertEquals("secret", configuration.getPassword());
+		assertEquals("mapping.ttl", configuration.getMappingFile());
+		assertEquals("output.ttl", configuration.getOutputFile());
+		assertEquals("TURTLE", configuration.getFormat());
+		assertEquals("http://www.example.org/", configuration.getBaseIRI());
+	}
+	
+	public void testExample12() throws R2RMLException {
+		Main.main(new String[] { "./test/resources/12.config.properties" });
+		Model output = ModelFactory.createDefaultModel();
+		output.read("./test/resources/12.output.ttl");
+		Model compare = ModelFactory.createDefaultModel();
+		compare.read("./test/resources/12.compareoutput.ttl", "TURTLE");
+		assertEquals(true, output.difference(compare).isEmpty());
+		assertEquals(true, compare.difference(output).isEmpty());
+	}
+	
+	public void testExample13() throws R2RMLException {
+		Main.main(new String[] { "./test/resources/13.config.properties" });
+		// Model output = ModelFactory.createDefaultModel();
+		// output.read("./test/resources/13.output.ttl");
+		// Model compare = ModelFactory.createDefaultModel();
+		// compare.read("./test/resources/12.compareoutput.ttl", "TURTLE");
+		// assertEquals(true, output.difference(compare).isEmpty());
+		// assertEquals(true, compare.difference(output).isEmpty());
+	}
+
+}

+ 201 - 0
test/test/TestR2RMLMySQL.java

@@ -0,0 +1,201 @@
+package test;
+
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.BeforeClass;
+
+import junit.framework.TestCase;
+import r2rml.engine.Configuration;
+import r2rml.engine.R2RMLProcessor;
+
+/**
+ * Unit test for MySQL database "r2rml". Requires a user "foo" with password 
+ * "bar". A MySQL dump file can be found in the resources folder.
+ *  
+ * @author Christophe Debruyne
+ *
+ */
+public class TestR2RMLMySQL extends TestCase {
+
+	private static String connectionURL = "jdbc:mysql://localhost/r2rml";
+
+	public TestR2RMLMySQL(String testName) {
+		super(testName);
+	}
+
+	@BeforeClass
+	public static void init() throws Exception {
+		// Log4J junit configuration.
+		BasicConfigurator.configure();
+	}
+
+	/**
+	 * 2.3 Example: Mapping a Simple Table
+	 * https://www.w3.org/TR/r2rml/#example-simple
+	 */
+	public void testExample01() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/01.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/01.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());	
+	}
+
+	/**
+	 * 2.4 Example: Computing a Property with an R2RML View
+	 */
+	public void testExample02() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/02.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/02.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());	
+	}
+
+	/**
+	 * 2.5 Example: Linking Two Tables
+	 */
+	public void testExample03() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/03.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/03.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	/**
+	 * Many-to-Many 1
+	 */
+	public void testExample04() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/04.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/04.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	/**
+	 * Many-to-Many 2
+	 */
+	public void testExample05() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/05.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/05.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample06() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/06.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/06.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample07() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/07.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/07.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample08() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/08.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/08.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample09() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/09.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/09.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+	public void testExample10() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/10.mapping.ttl");
+		configuration.setUser("foo");
+		configuration.setPassword("bar");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		// HAD TO CREATE AN RDF/XML FILE AS JENA IS UNABLE TO PARSE "\o" AS TTL
+		// IT THINKS YOU ARE TRYING TO ESCAPE AN "o"... NO PROBLEM WITH RDF/XML
+		target.read("./test/resources/10.output.rdf");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());
+	}
+
+}