You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by wi...@apache.org on 2013/02/19 13:51:59 UTC
[48/52] [partial] code contribution,
initial import of relevant modules of LMF-3.0.0-SNAPSHOT based on
revision 4bf944319368 of the default branch at https://code.google.com/p/lmf/
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
new file mode 100644
index 0000000..229a306
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
@@ -0,0 +1,401 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.marmotta.kiwi.reasoner.sail;
+
+import info.aduna.iteration.CloseableIteration;
+import info.aduna.iteration.ExceptionConvertingIteration;
+import org.apache.marmotta.kiwi.reasoner.engine.ReasoningConfiguration;
+import org.apache.marmotta.kiwi.reasoner.engine.ReasoningEngine;
+import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
+import org.apache.marmotta.kiwi.reasoner.parser.ParseException;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.wrapper.TransactionalSailWrapper;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.StackableSail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This sail adds the KWRL reasoner to the stack of sails. Because the reasoner is tightly coupled with the
+ * database schema of KiWi, it requires that the persistence on the root of the stack is a KiWiStore.
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class KiWiReasoningSail extends TransactionalSailWrapper {
+
+ private static Logger log = LoggerFactory.getLogger(KiWiReasoningSail.class);
+
+ private ReasoningConfiguration config;
+
+ private ReasoningEngine engine;
+
+ private KiWiReasoningPersistence persistence;
+
+ private boolean initialized = false;
+
+ public KiWiReasoningSail(TransactionalSail parent, ReasoningConfiguration config) {
+ super(parent);
+ this.config = config;
+ }
+
+ @Override
+ public void initialize() throws SailException {
+ synchronized (this) {
+ if(!initialized) {
+ super.initialize();
+
+ KiWiStore store = getBaseStore();
+
+ try {
+ persistence = new KiWiReasoningPersistence(store.getPersistence(), getValueFactory());
+ persistence.initDatabase();
+
+ engine = new ReasoningEngine(persistence,this,config);
+ addTransactionListener(engine);
+
+ initialized = true;
+ } catch (SQLException e) {
+ log.error("error initializing reasoning database",e);
+ throw new SailException("error initializing reasoning database",e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void shutDown() throws SailException {
+ engine.shutdown();
+ super.shutDown();
+ }
+
+ /**
+ * Return the KiWi store that is at the base of the SAIL stack. Throws an IllegalArgumentException in case the base
+ * store is not a KiWi store.
+ *
+ * @return
+ */
+ public KiWiStore getBaseStore() {
+ StackableSail current = this;
+ while(current != null && current.getBaseSail() instanceof StackableSail) {
+ current = (StackableSail) current.getBaseSail();
+ }
+ if(current != null && current.getBaseSail() instanceof KiWiStore) {
+ return (KiWiStore) current.getBaseSail();
+ } else {
+ throw new IllegalStateException("the base store is not a KiWiStore (type: "+current.getBaseSail().getClass().getCanonicalName()+")!");
+ }
+ }
+
+ public ReasoningConfiguration getConfig() {
+ return config;
+ }
+
+ /**
+ * Add a program to the reasoner using the given name. The program data will be read from the stream passed as
+ * second argument. The program is persisted to the database and the reasoning engine is
+ * notified of the added rules and immediately calculates the inferences. Inferencing in this case is
+ * synchronous, so the method only returns when the first round of reasoning is completed for all added
+ * rules.
+ * <p/>
+ * If a program with this name already exists, a SailException is thrown. To update existing programs,
+ * please use updateProgram().
+ *
+ * @param name a unique name for the program
+ * @param data the program data in KWRL syntax
+ * @throws IOException in case the stream cannot be read
+ * @throws SailException in case the program already exists
+ * @throws ParseException in case the program cannot be parsed
+ */
+ public void addProgram(String name, InputStream data) throws IOException, SailException, ParseException {
+ KWRLProgramParserBase parser = new KWRLProgramParser(getValueFactory(), data);
+ Program p = parser.parseProgram();
+ p.setName(name);
+
+ addProgram(p);
+ }
+
+ /**
+ * Add a program to the reasoner. The program is persisted to the database and the reasoning engine is
+ * notified of the added rules and immediately calculates the inferences. Inferencing in this case is
+ * synchronous, so the method only returns when the first round of reasoning is completed for all added
+ * rules.
+ * <p/>
+ * If a program with this name already exists, a SailException is thrown. To update existing programs,
+ * please use updateProgram().
+ *
+ * @param program the program data in KWRL syntax
+ * @throws SailException in case the program already exists
+ */
+ public void addProgram(Program program) throws SailException {
+ // store program in the database
+ try {
+ KiWiReasoningConnection connection = persistence.getConnection();
+ try {
+ // should not throw an exception and the program should have a database ID afterwards
+ connection.storeProgram(program);
+ connection.commit();
+ } finally {
+ connection.close();
+ }
+ } catch (SQLException ex) {
+ throw new SailException("cannot store program in database",ex);
+ }
+
+ engine.loadPrograms();
+
+ // now add all added rules to the reasoner
+ for(Rule rule : program.getRules()) {
+ engine.notifyAddRule(rule);
+ }
+ }
+
+ /**
+ * Update the program with the name given as argument using the data provided in the stream.
+ * This method will first calculate the difference between the
+ * previous version of the program and the new version of the program. It then updates the program in
+ * the database and notifies the engine of all removed and added rules.
+ *
+ * @throws IOException in case the stream cannot be read
+ * @throws SailException in case the program already exists
+ * @throws ParseException in case the program cannot be parsed
+ */
+ public void updateProgram(String name, InputStream data) throws IOException, SailException, ParseException {
+ KWRLProgramParserBase parser = new KWRLProgramParser(getValueFactory(), data);
+ Program p = parser.parseProgram();
+ p.setName(name);
+
+ updateProgram(p);
+ }
+
+
+ /**
+ * Update the program given as argument. This method will first calculate the difference between the
+ * previous version of the program and the new version of the program. It then updates the program in
+ * the database and notifies the engine of all removed and added rules.
+ *
+ * @param program the updated version of the program
+ * @throws SailException in case a database error occurs
+ */
+ public void updateProgram(Program program) throws SailException {
+ Set<Rule> added = new HashSet<Rule>();
+ Set<Rule> removed = new HashSet<Rule>();
+ try {
+ KiWiReasoningConnection connection = persistence.getConnection();
+ try {
+ // load old version of program and calculate difference
+ Program old = connection.loadProgram(program.getName());
+ if(old != null) {
+ for(Rule r : old.getRules()) {
+ if(!program.getRules().contains(r)) {
+ removed.add(r);
+ }
+ }
+ for(Rule r : program.getRules()) {
+ if(!old.getRules().contains(r)) {
+ added.add(r);
+ }
+ }
+
+ }
+
+ // store program in the database
+ connection.updateProgram(program);
+ connection.commit();
+ } finally {
+ connection.close();
+ }
+ } catch (SQLException ex) {
+ throw new SailException("cannot store program in database",ex);
+ }
+
+ engine.loadPrograms();
+
+ // if rules have been removed, clean up
+ if(removed.size() > 0) {
+ engine.notifyRemoveRules();
+ }
+
+ // now add all added rules to the reasoner
+ for(Rule rule : added) {
+ engine.notifyAddRule(rule);
+ }
+ }
+
+ /**
+ * List all reasoning programs currently stored in the triplestore.
+ *
+ * @return
+ */
+ public CloseableIteration<Program,SailException> listPrograms() throws SailException {
+ try {
+ final KiWiReasoningConnection connection = persistence.getConnection();
+
+ return new ExceptionConvertingIteration<Program, SailException>(connection.listPrograms()) {
+ /**
+ * Converts an exception from the underlying iteration to an exception of
+ * type <tt>X</tt>.
+ */
+ @Override
+ protected SailException convert(Exception e) {
+ return new SailException(e);
+ }
+
+ @Override
+ protected void handleClose() throws SailException {
+ super.handleClose();
+
+ try {
+ connection.commit();
+ connection.close();
+ } catch (SQLException ex) {
+ throw new SailException("database error while committing/closing connection");
+ }
+ }
+ };
+ } catch (SQLException ex) {
+ throw new SailException("cannot list programs in database",ex);
+ }
+
+ }
+
+
+ /**
+ * Return the program with the given name. In case the program does not exist, the method will
+ * return null.
+ *
+ * @param name the unique name of the program to retrieve
+ * @return the parsed program, or null in case a program with the given name does not exist
+ * @throws SailException in case an error occurs
+ */
+ public Program getProgram(String name) throws SailException {
+ try {
+ KiWiReasoningConnection connection = persistence.getConnection();
+ try {
+ // should not throw an exception and the program should have a database ID afterwards
+ Program p = connection.loadProgram(name);
+ connection.commit();
+ return p;
+ } finally {
+ connection.close();
+ }
+ } catch (SQLException ex) {
+ throw new SailException("cannot load program from database",ex);
+ }
+ }
+
+
+ /**
+ * Remove the program with the given name. This method will first remove the program from the database and
+ * then inform the reasoning engine to run cleanups.
+ * <p/>
+ * If a program with this name does not exist, does nothing
+ *
+ * @param name the unique name of the program to remove
+ * @throws SailException
+ */
+ public void deleteProgram(String name) throws SailException {
+ try {
+ KiWiReasoningConnection connection = persistence.getConnection();
+ try {
+ Program p = connection.loadProgram(name);
+ connection.deleteProgram(p);
+ connection.commit();
+ } finally {
+ connection.close();
+ }
+ } catch (SQLException ex) {
+ throw new SailException("cannot load program from database",ex);
+ }
+ engine.loadPrograms();
+ engine.notifyRemoveRules();
+ }
+
+
+ /**
+ * Clean all inferred triples and re-run all reasoning rules.
+ */
+ public void reRunPrograms() {
+ engine.reRunPrograms();
+ }
+
+ /**
+ * Return a reference to the underlying reasoning engine.
+ * @return
+ */
+ public ReasoningEngine getEngine() {
+ return engine;
+ }
+
+ /**
+ * Return a reference to the underlying database persistence layer.
+ * @return
+ */
+ public KiWiReasoningPersistence getPersistence() {
+ return persistence;
+ }
+
+ /**
+ * List the justifications for the triple with the id given as argument. For informational purposes.
+ *
+ * @param tripleId
+ * @return
+ * @throws SailException
+ */
+ public CloseableIteration<Justification,SailException> justify(long tripleId) throws SailException {
+ try {
+ final KiWiReasoningConnection connection = persistence.getConnection();
+
+ return new ExceptionConvertingIteration<Justification, SailException>(connection.listJustificationsForTriple(tripleId)) {
+ /**
+ * Converts an exception from the underlying iteration to an exception of
+ * type <tt>X</tt>.
+ */
+ @Override
+ protected SailException convert(Exception e) {
+ return new SailException(e);
+ }
+
+ @Override
+ protected void handleClose() throws SailException {
+ super.handleClose();
+
+ try {
+ connection.commit();
+ connection.close();
+ } catch (SQLException ex) {
+ throw new SailException("database error while committing/closing connection");
+ }
+ }
+ };
+ } catch (SQLException ex) {
+ throw new SailException("cannot list programs in database",ex);
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj b/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
new file mode 100644
index 0000000..0d033bf
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** JavaCC parser of simple KWRL programs.
+ *
+ * Simplified for the Linked Media Framework (2011), adapted for Apache Marmotta (2013).
+ *
+ *
+ * @author Jakub Kotowski
+ * @author Sebastian Schaffert
+ *
+ */
+ // ($1 http://rdf.com/type $2), ($1 http://rdf.com/subclass $3) -> ($3 http://rdf.com/superclass http://zoo.org/animal)
+ // (http://www.seznam.cz/subj http://www.seznam.cz/prop http://www.seznam.cz/obj) -> (http://sign-o-matic.com/ont#subj http://sign-o-matic.com/ont#prop http://sign-o-matic.com/ont#obj)
+ //($1 http://ex.com/p $2), ($2 http://ex.com/p $1), ($1 http://ex.com/sc "ahoj kocko") -> ($4 $5 'a tady zkusime neco delsiho a hroznejsiho):""#$@%///...')
+ //($1 http://ex.com/p $2), ($2 http://ex.com/p $1), ($1 http://ex.com/sc "ahoj kocko") -> ($4 $5 "a tady zkusime neco delsiho a hroznejsiho):#$@%///..."^^http://example.com/type#string)
+ //($1 http://ex.com/p $2) -> (uri(http://ex.com/p) @ $3 $4 $5)
+ //($1 http://ex.com/p $2) -> (uri(http://ex.com/p) @ $3 $4 uri(http://www.kiwi-project.eu/core/tagging/) @ $5)
+
+ /**
+@prefix rdf: <http://w3c.org/rdf/>
+@prefix rdfs: <http://w3c.org/rdfs/pat/hayes/mt#>
+
+(($1 http://rdf.com/type $2) , ($1 http://rdf.com/subclass $3)) -> ($3 http://rdf.com/superclass http://zoo.org/animal)
+(($1 rdf:type $2) , ($1 rdf:subclass $3)) -> ($3 rdf:superclass xxx:animal)
+rdf-subclass-transitivity: (($c1 rdfs:subClassOf $c2) , ($c2 rdfs:subClassOf $c3)) -> ($c1 rdfs:subClassOf $c3),($3 rdf:superclass xxx:animal)
+(($1 rdf:type $1') , ($1' rdf:subclass $3)) -> ($3 rdf:superclass xxx:animal)
+
+(($1 rdf:type $1');($2 rdf:type $2')), not ($3 rdf:type $3') ,
+(($3 rdf:type $3'),not ($3 rdf:type $3'))
+ -> inconsistency
+ */
+
+options {
+ STATIC=false;
+ KEEP_LINE_COLUMN = false;
+ CACHE_TOKENS=true;
+// FORCE_LA_CHECK=true;
+// CHOICE_AMBIGUITY_CHECK=5;
+ LOOKAHEAD=2147483647;
+// DEBUG_PARSER=true;
+// DEBUG_TOKEN_MANAGER=true;
+// DEBUG_LOOKAHEAD=true;
+}
+
+PARSER_BEGIN(KWRLProgramParser)
+package org.apache.marmotta.kiwi.reasoner.parser;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.InputStream;
+import org.openrdf.model.*;
+import java.util.*;
+import org.apache.marmotta.kiwi.reasoner.model.program.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class KWRLProgramParser extends KWRLProgramParserBase {
+
+ public KWRLProgramParser(Reader in, ValueFactory valueFactory) {
+ this(in);
+
+ setValueFactory(valueFactory);
+ }
+
+ public KWRLProgramParser(ValueFactory valueFactory, String content) {
+ this(new StringReader(content));
+
+ setValueFactory(valueFactory);
+ }
+
+ public KWRLProgramParser(ValueFactory valueFactory, InputStream in) {
+ this(in);
+
+ setValueFactory(valueFactory);
+ }
+
+ public KWRLProgramParser(ValueFactory valueFactory, InputStream in, String encoding) {
+ this(in, encoding);
+
+ setValueFactory(valueFactory);
+ }
+
+
+}
+PARSER_END(KWRLProgramParser)
+
+SKIP :
+{
+ " "
+| "\r"
+| "\t"
+| "\n"
+}
+
+MORE:
+{
+ "\"" : WithinString
+}
+
+<WithinString> TOKEN:
+{
+ <STRLIT: "\""> : DEFAULT
+}
+
+<WithinString> MORE:
+{
+ <~["\n","\r"]>
+}
+
+TOKEN : /* OPERATORS */
+{
+ < AND: "," > |
+ < OR: ";" > |
+ < NOT: "not" > |
+ < THEN: "->" > |
+ < TYPE: "^^" >
+}
+
+TOKEN :
+{
+ < LEFTP: "(" > |
+ < RIGHTP: ")" > |
+ < INCONSISTENCY: "inconsistency" > |
+ //URICHARS should oficially include "(" and ")", I excluded them because of resulting conflicts with the rule grammar
+ //< URICHARS: ["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"] > |
+ // URI is oficially more general see the comment below
+ //TODO: parser now allows URIs ending with # so that the same URI token spec can be used for namespaces too
+ < URI: ["a"-"z","A"-"Z"](["a"-"z","A"-"Z","0"-"9","+","-","."])* "://" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+ ("#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])*)? | "#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+> |
+ < NS_URI: <IDENTIFIER> ":" (<URICHAR>)+ > |
+ < VARIABLE: "$" <IDENTIFIER> > |
+ < URICONSTRUCTION: "uri" > |
+ < IDENTIFIER: ["a"-"z","A"-"Z","0"-"9","_"](["a"-"z","A"-"Z","0"-"9","_","'","-"])* > |
+ < #URICHAR: ["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"] >
+}
+//https://javacc.dev.java.net/doc/javaccgrm.html
+// If the label is preceded by a "#", then this regular expression may not be referred to from expansion units, but only from within other regular expressions. When the "#" is present, the regular expression is referred to as a "private regular expression".
+// If the "#" is omitted, the token manager will erroneously recognize a string like E123 as a legal token of kind EXPONENT (instead of IDENTIFIER in the Java grammar).
+
+// (([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)?/{0,2}[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)? (#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?
+// http://aspn.activestate.com/ASPN/Mail/Message/xml-dev/754445
+
+Program Program() :
+{
+ Program program = new Program();
+ int nullNameCount = 0;
+ Rule rule;
+ Token name = null;
+ Token prefix = null;
+ Token uri;
+}
+{
+ (
+ "@prefix" prefix = <IDENTIFIER> ":" "<" uri = <URI> ">" (";")? {
+ program.addNamespace(prefix.image, uri.image );
+ namespaces.put(prefix.image, uri.image);
+ }
+ )*
+
+ (
+ (name = <IDENTIFIER> ":")? rule = Rule()
+ {
+ if (name == null) {
+ nullNameCount++;
+ rule.setName("rule_" + nullNameCount);
+ } else {
+ rule.setName(name.image);
+ }
+
+ program.addRule(rule); name=null; //it's declared above, once set it doesn't get cleared
+ }
+ )+
+ <EOF>
+ {
+ return program;
+ }
+}
+
+
+Rule Rule() : {
+ Pattern head;
+ List<Pattern> body;
+ Rule rule = new Rule();
+ startRule();
+}
+{
+ body = Body() <THEN> head = HeadTriplePattern() (";"|".")?
+ {
+ rule.setHead(head);
+ rule.setBody(body);
+ endRule();
+ return rule;
+ }
+}
+
+
+List<Pattern> Body() : {
+ Pattern pattern, pattern2;
+ List<Pattern> body = new LinkedList<Pattern>();
+}
+{
+ pattern = BodyTriplePattern() { body.add(pattern); } ( "," pattern2 = BodyTriplePattern() { body.add(pattern2); } )*
+ { return body; }
+}
+
+
+Pattern BodyTriplePattern() : {Field subject; Field property; Field object; }
+{
+ <LEFTP> subject = Subject() property = Property() object = BodyObject() <RIGHTP>
+ {
+ return new Pattern(subject, property, object);
+ }
+}
+
+Pattern HeadTriplePattern() : {Field subject; Field property; Field object; }
+{
+ <LEFTP> subject = HeadSubject() property = Property() object = HeadObject() <RIGHTP>
+ {
+ return new Pattern(subject, property, object);
+ }
+}
+
+Field HeadSubject() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+ uri = Uri() | variable = Variable()
+)
+ {
+ if (uri != null) {
+ return uri;
+ }
+
+ return variable;
+ }
+}
+
+Field Property() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+ uri = Uri() | variable = Variable()
+)
+ {
+ if (uri != null)
+ return uri;
+
+ return variable;
+ }
+}
+
+Field HeadObject() : { ResourceField uri = null; VariableField variable = null; LiteralField literal = null; }
+{
+ (
+ uri = Uri() | variable = Variable() | literal = Literal()
+ )
+ {
+ if (uri != null)
+ return uri;
+
+ if (variable != null)
+ return variable;
+
+ return literal;
+ }
+}
+
+Field Subject() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+ uri = Uri() | variable = Variable()
+)
+ {
+ if (uri != null)
+ return uri;
+
+ if (variable != null)
+ return variable;
+ }
+}
+
+Field BodyObject() : { ResourceField uri = null; VariableField variable = null; LiteralField literal = null; }
+{
+ (
+ uri = Uri() | variable = Variable() | literal = Literal()
+ )
+ {
+ if (uri != null)
+ return uri;
+
+ if (variable != null)
+ return variable;
+
+ return literal;
+ }
+}
+
+ResourceField Uri() : { Token uri; }
+{
+ (
+ "<" uri = <URI> ">"
+ {
+ return getResource(uri.image);
+ }
+ | uri = <URI> // backwards compatibility
+ {
+ log.warn("DEPRECATION: please enclose URIs always in < and >; URI was: {}", uri.image);
+ return getResource(uri.image);
+ }
+ | uri = <NS_URI>
+ {
+ return getResourceByNS(uri.image);
+ }
+ )
+}
+
+VariableField Variable() : {Token t;}
+{
+ t = <VARIABLE>
+ {
+ return getVariable(t.image);
+ }
+}
+
+//TODO language tags
+LiteralField Literal() : {Token literal = null; Token type = null;}
+{
+(
+ literal = <STRLIT> (<TYPE> type = <URI>)? | "\"\""
+){ //have to remove the leading " character first
+ if (type != null) {
+ return new LiteralField(resolveLiteral(literal.image.substring(1, literal.image.length()-1), Locale.getDefault(), type.image));
+ }
+
+ return new LiteralField(resolveLiteral(literal.image.substring(1, literal.image.length()-1), Locale.getDefault(), null));
+ }
+
+}
+
+
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
new file mode 100644
index 0000000..cb24c14
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
@@ -0,0 +1,62 @@
+CREATE SEQUENCE seq_programs;
+CREATE SEQUENCE seq_rules;
+CREATE SEQUENCE seq_justifications;
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+ id bigint NOT NULL,
+ name varchar(64) NOT NULL,
+ description TEXT,
+ PRIMARY KEY (id)
+);
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+ id bigint NOT NULL,
+ name varchar(64),
+ description TEXT,
+ body TEXT NOT NULL,
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+);
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+ ns_prefix VARCHAR(64) NOT NULL,
+ ns_uri VARCHAR(2048) NOT NULL
+);
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+ id bigint NOT NULL,
+ triple_id bigint NOT NULL REFERENCES triples(id),
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+);
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ triple_id bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+);
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
new file mode 100644
index 0000000..00109e5
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple;
+DROP INDEX idx_just_supp_rules_just;
+DROP INDEX idx_just_supp_rules_rule;
+DROP INDEX idx_just_supp_triples_just;
+DROP INDEX idx_just_supp_triples_triple;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP SEQUENCE IF EXISTS seq_rules;
+DROP SEQUENCE IF EXISTS seq_justifications;
+DROP SEQUENCE IF EXISTS seq_programs;
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
new file mode 100644
index 0000000..36677ee
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules = SELECT nextval('seq_rules')
+seq.programs = SELECT nextval('seq_programs')
+seq.justifications = SELECT nextval('seq_justifications')
+
+# rules
+rules.insert = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+ FROM reasoner_rules R, reasoner_program_rules J \
+ WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+ WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list = SELECT id, name, description FROM reasoner_programs
+programs.delete = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_triples T \
+ WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_rules R \
+ WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+ FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
new file mode 100644
index 0000000..d81ea21
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
@@ -0,0 +1,75 @@
+CREATE TABLE seq_programs (id BIGINT NOT NULL);
+INSERT INTO seq_programs(id) VALUES (0);
+
+CREATE TABLE seq_rules (id BIGINT NOT NULL);
+INSERT INTO seq_rules VALUES (0);
+
+CREATE TABLE seq_justifications (id BIGINT NOT NULL);
+INSERT INTO seq_justifications(id) VALUES (0);
+
+-- Sequences in MySQL:
+-- UPDATE sequence SET id=LAST_INSERT_ID(id+1);
+-- SELECT LAST_INSERT_ID();
+
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+ id bigint NOT NULL,
+ name varchar(64) NOT NULL,
+ description TEXT,
+ PRIMARY KEY (id)
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+ id bigint NOT NULL,
+ name varchar(64),
+ description TEXT,
+ body TEXT NOT NULL,
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+ ns_prefix VARCHAR(64) NOT NULL,
+ ns_uri VARCHAR(2048) NOT NULL,
+ PRIMARY KEY (program_id, ns_prefix)
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+ id bigint NOT NULL,
+ triple_id bigint NOT NULL REFERENCES triples(id),
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ triple_id bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+) CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
new file mode 100644
index 0000000..992acbc
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple ON reasoner_justifications;
+DROP INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules;
+DROP INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules;
+DROP INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples;
+DROP INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP TABLE IF EXISTS seq_rules;
+DROP TABLE IF EXISTS seq_justifications;
+DROP TABLE IF EXISTS seq_programs;
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
new file mode 100644
index 0000000..c559dd3
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
@@ -0,0 +1,78 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules.prep = UPDATE seq_rules SET id=LAST_INSERT_ID(id+1);
+seq.rules = SELECT LAST_INSERT_ID();
+
+seq.programs.prep = UPDATE seq_programs SET id=LAST_INSERT_ID(id+1);
+seq.programs = SELECT LAST_INSERT_ID();
+
+seq.justifications.prep= UPDATE seq_justifications SET id=LAST_INSERT_ID(id+1);
+seq.justifications = SELECT LAST_INSERT_ID();
+
+
+# rules
+rules.insert = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+ FROM reasoner_rules R, reasoner_program_rules J \
+ WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+ WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list = SELECT id, name, description FROM reasoner_programs
+programs.delete = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_triples T \
+ WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_rules R \
+ WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+ FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
new file mode 100644
index 0000000..e9d5384
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
@@ -0,0 +1,64 @@
+CREATE SEQUENCE seq_programs;
+CREATE SEQUENCE seq_rules;
+CREATE SEQUENCE seq_justifications;
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+ id bigint NOT NULL,
+ name varchar(64) NOT NULL,
+ description TEXT,
+ PRIMARY KEY (id)
+);
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+ id bigint NOT NULL,
+ name varchar(64),
+ description TEXT,
+ body TEXT NOT NULL,
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+);
+
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+ ns_prefix VARCHAR(64) NOT NULL,
+ ns_uri VARCHAR(2048) NOT NULL,
+ PRIMARY KEY (program_id, ns_prefix)
+);
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+ program_id bigint NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE ,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+ id bigint NOT NULL,
+ triple_id bigint NOT NULL REFERENCES triples(id),
+ createdAt TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+);
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ triple_id bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+);
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+ justification_id bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+ rule_id bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
new file mode 100644
index 0000000..00109e5
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple;
+DROP INDEX idx_just_supp_rules_just;
+DROP INDEX idx_just_supp_rules_rule;
+DROP INDEX idx_just_supp_triples_just;
+DROP INDEX idx_just_supp_triples_triple;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP SEQUENCE IF EXISTS seq_rules;
+DROP SEQUENCE IF EXISTS seq_justifications;
+DROP SEQUENCE IF EXISTS seq_programs;
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
new file mode 100644
index 0000000..4517e3c
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules = SELECT nextval('seq_rules')
+seq.programs = SELECT nextval('seq_programs')
+seq.justifications = SELECT nextval('seq_justifications')
+
+# rules
+rules.insert = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+ FROM reasoner_rules R, reasoner_program_rules J \
+ WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+ WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list = SELECT id, name, description FROM reasoner_programs
+programs.delete = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_triples T \
+ WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+ FROM reasoner_justifications J, reasoner_just_supp_rules R \
+ WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+ FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
new file mode 100644
index 0000000..d58e40b
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.engine;
+
+import info.aduna.iteration.Iterations;
+import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
+import org.apache.marmotta.kiwi.persistence.KiWiDialect;
+import org.apache.marmotta.kiwi.persistence.KiWiPersistence;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
+import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.reasoner.engine.ReasoningConfiguration;
+import org.apache.marmotta.kiwi.reasoner.engine.ReasoningEngine;
+import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.helper.DBConnectionChecker;
+import org.apache.marmotta.kiwi.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.model.TransactionData;
+import org.apache.marmotta.kiwi.transactions.sail.KiWiTransactionalSail;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.openrdf.model.Resource;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.rio.RDFFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.hamcrest.Matchers.hasItem;
+
+/**
+ * This test verifies the functionality of the KiWi Reasonong Engine. Based on a small sample program, it will test
+ * both incremental reasoning (by manually adding triples) and full reasoning. After reasoning completes, it will
+ * check if the expected inferred triples as well as their justifications are present.
+ * <p/>
+ * It will try running over all available databases. Except for in-memory databases like H2 or Derby, database
+ * URLs must be passed as system property, or otherwise the test is skipped for this database. Available system properties:
+ * <ul>
+ * <li>PostgreSQL:
+ * <ul>
+ * <li>postgresql.url, e.g. jdbc:postgresql://localhost:5433/kiwitest?prepareThreshold=3</li>
+ * <li>postgresql.user (default: lmf)</li>
+ * <li>postgresql.pass (default: lmf)</li>
+ * </ul>
+ * </li>
+ * <li>MySQL:
+ * <ul>
+ * <li>mysql.url, e.g. jdbc:mysql://localhost:3306/kiwitest?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull</li>
+ * <li>mysql.user (default: lmf)</li>
+ * <li>mysql.pass (default: lmf</li>
+ * </ul>
+ * </li>
+ * <li>H2:
+ * <ul>
+ * <li>h2.url, e.g. jdbc:h2:mem;MVCC=true;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=10</li>
+ * <li>h2.user (default: lmf)</li>
+ * <li>h2.pass (default: lmf</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @see org.apache.marmotta.kiwi.persistence.KiWiConnection
+ * @see org.apache.marmotta.kiwi.persistence.KiWiPersistence
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+@RunWith(Parameterized.class)
+public class ReasoningEngineTest {
+
+ private static Logger log = LoggerFactory.getLogger(ReasoningEngineTest.class);
+
+ private static final String NS = "http://localhost/resource/";
+
+ /**
+ * Return database configurations if the appropriate parameters have been set.
+ *
+ * @return an array (database name, url, user, password)
+ */
+ @Parameterized.Parameters(name="Database Test {index}: {0} at {1}")
+ public static Iterable<Object[]> databases() {
+ String[] databases = {"H2", "PostgreSQL", "MySQL"};
+
+ List<Object[]> result = new ArrayList<Object[]>(databases.length);
+ for(String database : databases) {
+ if(System.getProperty(database.toLowerCase()+".url") != null) {
+ result.add(new Object[] {
+ database,
+ System.getProperty(database.toLowerCase()+".url"),
+ System.getProperty(database.toLowerCase()+".user","lmf"),
+ System.getProperty(database.toLowerCase()+".pass","lmf")
+ });
+ }
+ }
+ return result;
+ }
+
+
+ private KiWiDialect dialect;
+
+ private String jdbcUrl;
+
+ private String jdbcUser;
+
+ private String jdbcPass;
+
+ private KiWiStore store;
+ private TransactionalSail tsail;
+ private KiWiPersistence persistence;
+ private KiWiReasoningPersistence rpersistence;
+ private ReasoningEngine engine;
+
+ private Repository repository;
+
+
+ public ReasoningEngineTest(String database, String jdbcUrl, String jdbcUser, String jdbcPass) {
+ this.jdbcPass = jdbcPass;
+ this.jdbcUrl = jdbcUrl;
+ this.jdbcUser = jdbcUser;
+
+ if("H2".equals(database)) {
+ this.dialect = new H2Dialect();
+ } else if("MySQL".equals(database)) {
+ this.dialect = new MySQLDialect();
+ } else if("PostgreSQL".equals(database)) {
+ this.dialect = new PostgreSQLDialect();
+ }
+
+ DBConnectionChecker.checkDatabaseAvailability(jdbcUrl, jdbcUser, jdbcPass, dialect);
+ }
+
+
+ @Before
+ public void initDatabase() throws Exception {
+
+ persistence = new KiWiPersistence("test",jdbcUrl,jdbcUser,jdbcPass,dialect);
+ persistence.initDatabase();
+
+
+ store = new KiWiStore("test",jdbcUrl,jdbcUser,jdbcPass,dialect, "http://localhost/context/default", "http://localhost/context/inferred");
+ tsail = new KiWiTransactionalSail(store);
+ repository = new SailRepository(tsail);
+ repository.initialize();
+
+ rpersistence = new KiWiReasoningPersistence(persistence, repository.getValueFactory());
+ rpersistence.initDatabase();
+
+
+ // store a program, very simple transitive and symmetric rules:
+ // ($1 ex:property $2), ($2 ex:property $3) -> ($1 ex:property $3)
+ // ($1 ex:symmetric $2) -> ($2 ex:symmetric $1)
+ KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream("simple.kwrl"));
+ Program p = parser.parseProgram();
+ p.setName("simple");
+
+ KiWiReasoningConnection connection = rpersistence.getConnection();
+ try {
+ // should not throw an exception and the program should have a database ID afterwards
+ connection.storeProgram(p);
+ connection.commit();
+ } finally {
+ connection.close();
+ }
+
+ // instantiate reasoning engine, will load the programs into memory
+ engine = new ReasoningEngine(rpersistence,tsail,new ReasoningConfiguration());
+
+ }
+
+ @After
+ public void dropDatabase() throws Exception {
+ engine.shutdown();
+
+ rpersistence.dropDatabase();
+
+ persistence.dropDatabase();
+ persistence.shutdown();
+
+ repository.shutDown();
+ }
+
+
+ /**
+ * Test the reasoning engine by incrementally adding and later removing triples through explicit calls to the
+ * reasoning engine methods. This test checks pure in-memory processing for rule2, which only contains a
+ * single query pattern.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testIncrementalReasoningMemory() throws Exception {
+ RepositoryConnection con = repository.getConnection();
+ KiWiReasoningConnection rcon = rpersistence.getConnection();
+ try {
+ con.begin();
+ // create a triple (ex:a ex:symmetric ex:b); this should trigger rule 2 of the reasoner and add the
+ // inverse relationship
+ Resource subject = con.getValueFactory().createURI(NS+"a");
+ URI property = con.getValueFactory().createURI(NS+"symmetric");
+ Resource object = con.getValueFactory().createURI(NS+"b");
+
+ con.add(subject,property,object);
+ con.commit();
+
+ // load the statement from the connection so we can add it to the reasoner
+ List<Statement> statements = Iterations.asList(con.getStatements(subject,property,object, false));
+ Assert.assertEquals(1,statements.size());
+
+ // add triple to engine
+ TransactionData data = new TransactionData();
+ data.getAddedTriples().add(statements.get(0));
+ engine.afterCommit(data);
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ // after the engine completes, we check whether
+ // 1) the expected inferred triple exists
+ // 2) the inferred triple is properly justified (based on rule2 and on the triple contained in the transaction data)
+ List<Statement> inferred = Iterations.asList(con.getStatements(object,property,subject, true));
+ Assert.assertEquals("number of inferred triples differs from expected result",1,inferred.size());
+
+ KiWiTriple triple = (KiWiTriple)inferred.get(0);
+ List<Justification> justifications = Iterations.asList(rcon.listJustificationsForTriple(triple));
+ Assert.assertEquals("number of justifications for triple differs from expected result",1,justifications.size());
+
+ Justification j = justifications.get(0);
+ Assert.assertEquals("number of supporting triples differs from expected result",1,j.getSupportingTriples().size());
+ Assert.assertEquals("number of supporting rules differs from expected result",1,j.getSupportingRules().size());
+
+ Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(0)));
+
+ con.commit();
+
+
+ // now remove again the base triple and inform the reasoner about it, as a consequence, the inferred
+ // triple should also be removed
+ con.remove(subject,property,object);
+ con.commit();
+ TransactionData data2 = new TransactionData();
+ data2.getRemovedTriples().add(statements.get(0));
+ engine.afterCommit(data2);
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ List<Statement> inferred2 = Iterations.asList(con.getStatements(object,property,subject, true));
+ Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred2.size());
+
+ con.commit();
+ rcon.commit();
+ } finally {
+ con.close();
+ rcon.close();
+ }
+ }
+
+
+ /**
+ * Test the reasoning engine by incrementally adding and later removing triples through explicit calls to the
+ * reasoning engine methods. This test checks rule1 and thus involves both, in-memory pattern matching and database
+ * queries.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testIncrementalReasoningConjunction() throws Exception {
+ RepositoryConnection con = repository.getConnection();
+ KiWiReasoningConnection rcon = rpersistence.getConnection();
+ try {
+ con.begin();
+ // create a triple (ex:a ex:symmetric ex:b); this should trigger rule 2 of the reasoner and add the
+ // inverse relationship
+ Resource a = con.getValueFactory().createURI(NS+"a");
+ URI property = con.getValueFactory().createURI(NS+"transitive");
+ Resource b = con.getValueFactory().createURI(NS+"b");
+ Resource c = con.getValueFactory().createURI(NS+"c");
+ Resource d = con.getValueFactory().createURI(NS+"d");
+
+ con.add(a,property,b);
+ con.add(b,property,c);
+ con.commit();
+
+ // load the statement from the connection so we can add it to the reasoner
+ List<Statement> statements = Iterations.asList(con.getStatements(null,property,null, false));
+ Assert.assertEquals(2,statements.size());
+
+ // add triples to engine
+ TransactionData data = new TransactionData();
+ data.getAddedTriples().addAll(statements);
+ engine.afterCommit(data);
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ // after the engine completes, we check whether
+ // 1) the expected inferred triple exists (must be (ex:a ex:transitive ex:c) )
+ // 2) the inferred triple is properly justified (based on rule2 and on the triple contained in the transaction data)
+ List<Statement> inferred = Iterations.asList(con.getStatements(a,property,c, true));
+ Assert.assertEquals("number of inferred triples differs from expected result",1,inferred.size());
+
+ KiWiTriple triple = (KiWiTriple)inferred.get(0);
+ List<Justification> justifications = Iterations.asList(rcon.listJustificationsForTriple(triple));
+ Assert.assertEquals("number of justifications for triple differs from expected result",1,justifications.size());
+
+ Justification j = justifications.get(0);
+ Assert.assertEquals("number of supporting triples differs from expected result",2,j.getSupportingTriples().size());
+ Assert.assertEquals("number of supporting rules differs from expected result",1,j.getSupportingRules().size());
+
+ Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(0)));
+ Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(1)));
+
+ con.commit();
+
+
+ // add another triple and check if the incremental reasoning works
+ con.add(c,property,d);
+ con.commit();
+
+ // load the statement from the connection so we can add it to the reasoner
+ List<Statement> statements2 = Iterations.asList(con.getStatements(c,property,d, false));
+ Assert.assertEquals(1, statements2.size());
+
+ // add triples to engine
+ TransactionData data2 = new TransactionData();
+ data2.getAddedTriples().addAll(statements2);
+ engine.afterCommit(data2);
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+
+ // after the engine completes, we check whether the expected inferred triples exist
+ // (must be (ex:a ex:transitive ex:d) and (ex:b ex:transitive ex:d) )
+ List<Statement> inferred2 = Iterations.asList(con.getStatements(a,property,d, true));
+ Assert.assertEquals("number of inferred triples differs from expected result", 1, inferred2.size());
+
+ List<Statement> inferred3 = Iterations.asList(con.getStatements(b,property,d, true));
+ Assert.assertEquals("number of inferred triples differs from expected result", 1, inferred3.size());
+
+
+ // now remove again the base triple and inform the reasoner about it, as a consequence, the inferred
+ // triple should also be removed
+ con.remove(c, property, d);
+ con.commit();
+ TransactionData data3 = new TransactionData();
+ data3.getRemovedTriples().add(statements2.get(0));
+ engine.afterCommit(data3);
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ List<Statement> inferred4 = Iterations.asList(con.getStatements(a,property,d, true));
+ Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred4.size());
+
+ List<Statement> inferred5 = Iterations.asList(con.getStatements(b,property,d, true));
+ Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred5.size());
+
+ con.commit();
+ rcon.commit();
+ } finally {
+ con.close();
+ rcon.close();
+ }
+ }
+
+
+ /**
+ * Test running a full reasoning over the triple store based on the simple program and the simple.ttl data file.
+ * Test if the expected triples are present. Since we are only evaluating a single reasoning round, we cannot
+ * expect more complicated triples that involve chaining.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testFullReasoning() throws Exception {
+ RepositoryConnection con = repository.getConnection();
+ KiWiReasoningConnection rcon = rpersistence.getConnection();
+ try {
+ // add some triples
+ con.begin();
+
+ Resource a = con.getValueFactory().createURI(NS+"a");
+ Resource b = con.getValueFactory().createURI(NS+"b");
+ Resource c = con.getValueFactory().createURI(NS+"c");
+ Resource d = con.getValueFactory().createURI(NS+"d");
+ URI t = con.getValueFactory().createURI(NS+"transitive");
+ URI s = con.getValueFactory().createURI(NS+"symmetric");
+
+ con.add(this.getClass().getResourceAsStream("simple.ttl"),"http://localhost/resource/", RDFFormat.TURTLE);
+ con.commit();
+
+ // run the full reasoner
+ engine.reRunPrograms();
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ // after reasoning is finished, we expect to find the following triples:
+
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,t,d,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,s,a,true));
+
+ con.commit();
+ } finally {
+ con.close();
+ rcon.close();
+ }
+
+ }
+
+
+ /**
+ * Test adding and removing rules to an already inferred state of the triple store. When a rule is added, all
+ * possible new inferences should be added to the inferred triples. When a rule is removed, all inferences
+ * based on this rule should also be removed.
+ *
+ * @throws Exception
+ */
+ //@Test
+ public void testAddRemoveRule() throws Exception {
+ RepositoryConnection con = repository.getConnection();
+ KiWiReasoningConnection rcon = rpersistence.getConnection();
+ try {
+ // add some triples
+ con.begin();
+
+ Resource a = con.getValueFactory().createURI(NS+"a");
+ Resource b = con.getValueFactory().createURI(NS+"b");
+ Resource c = con.getValueFactory().createURI(NS+"c");
+ Resource d = con.getValueFactory().createURI(NS+"d");
+ URI t = con.getValueFactory().createURI(NS+"transitive");
+ URI s = con.getValueFactory().createURI(NS+"symmetric");
+
+ con.add(this.getClass().getResourceAsStream("simple.ttl"),"http://localhost/resource/", RDFFormat.TURTLE);
+ con.commit();
+
+ // run the full reasoner
+ engine.reRunPrograms();
+
+ // wait for reasoning to complete
+ while(engine.isRunning()) {
+ log.debug("sleeping for 100ms to let engine finish processing ... ");
+ Thread.sleep(100);
+ }
+ con.begin();
+
+ // after reasoning is finished, we expect to find the following triples:
+
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,t,d,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,s,a,true));
+
+ con.commit();
+
+
+ // now we remove the rule2 (symmetric rule) from the program, update the program in the database and then
+ // inform the reasoning engine about the removed rule
+ Program p = rcon.loadProgram("simple");
+ Rule removed = null;
+ Iterator<Rule> it = p.getRules().iterator();
+ while(it.hasNext()) {
+ Rule r = it.next();
+ if(r.getName().equals("rule2")) {
+ it.remove();
+ removed = r;
+ }
+ }
+ Assert.assertNotNull("rule 2 not found in program", removed);
+ rcon.updateProgram(p);
+ rcon.commit();
+
+ engine.notifyRemoveRules();
+
+ // after removing, the inferred symmetric triple should be gone, but the others should still exist
+ con.begin();
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b, t, d, true));
+ Assert.assertFalse("unexpected inferred triple found", con.hasStatement(b, s, a, true));
+ con.commit();
+
+
+ // let's add the rule again to the program, update the database, and inform the engine
+ p.getRules().add(removed);
+ rcon.updateProgram(p);
+ rcon.commit();
+
+ engine.notifyAddRule(removed);
+
+ // after adding, the inferred symmetric triple should again be present
+ con.begin();
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,t,d,true));
+ Assert.assertTrue("expected inferred triple not found", con.hasStatement(b, s, a, true));
+ con.commit();
+
+ } finally {
+ con.close();
+ rcon.close();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
new file mode 100644
index 0000000..d60a5da
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.parser;
+
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.memory.MemoryStore;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+@RunWith(Parameterized.class)
+public class KWRLProgramParserTest {
+
+ private Repository repository;
+
+ private String filename;
+
+ @Parameterized.Parameters(name="KWRL Program Test {index}: {0}")
+ public static Collection<Object[]> data() {
+ ArrayList<Object[]> list = new ArrayList<Object[]>();
+ for(int i=1; i<=4; i++) {
+ list.add(new Object[] {"test-"+String.format("%03d",i)});
+ }
+ return list;
+ }
+
+
+ public KWRLProgramParserTest(String filename) {
+ this.filename = filename;
+ }
+
+ @Before
+ public void setup() throws Exception {
+ repository = new SailRepository(new MemoryStore());
+ repository.initialize();
+ }
+
+
+ @After
+ public void shutdown() throws Exception {
+ repository.shutDown();
+ }
+
+ @Test
+ public void testParseProgram() throws Exception {
+ KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream(filename+".kwrl"));
+ Program p = parser.parseProgram();
+
+ Assert.assertNotNull(p);
+ Assert.assertFalse(p.getRules().isEmpty());
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
new file mode 100644
index 0000000..b7a0d68
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.parser;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.memory.MemoryStore;
+
+/**
+ * Test parsing of individual rules
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class KWRLRuleParserTest {
+
+ private Repository repository;
+
+ @Before
+ public void setup() throws Exception {
+ repository = new SailRepository(new MemoryStore());
+ repository.initialize();
+ }
+
+
+ @After
+ public void shutdown() throws Exception {
+ repository.shutDown();
+ }
+
+ @Test
+ public void testRule1() throws Exception {
+ String rule = "($1 http://www.w3.org/2000/01/rdf-schema#subClassOf $2), ($2 http://www.w3.org/2000/01/rdf-schema#subClassOf $3) -> ($1 http://www.w3.org/2000/01/rdf-schema#subClassOf $3)";
+ Rule r = KWRLProgramParser.parseRule(rule,repository.getValueFactory());
+
+ Assert.assertNotNull(r);
+ Assert.assertEquals(2, r.getBody().size());
+ Assert.assertTrue(r.getHead().getSubject().isVariableField());
+ Assert.assertTrue(r.getHead().getProperty().isResourceField());
+ Assert.assertTrue(r.getHead().getObject().isVariableField());
+ }
+
+ @Test
+ public void testRule2() throws Exception {
+ String rule = "($1 rdfs:subClassOf $2), ($2 rdfs:subClassOf $3) -> ($1 rdfs:subClassOf $3)";
+ Rule r = KWRLProgramParser.parseRule(rule, ImmutableMap.of("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), repository.getValueFactory());
+
+ Assert.assertNotNull(r);
+ Assert.assertEquals(2, r.getBody().size());
+ Assert.assertTrue(r.getHead().getSubject().isVariableField());
+ Assert.assertTrue(r.getHead().getProperty().isResourceField());
+ Assert.assertTrue(r.getHead().getObject().isVariableField());
+ }
+
+}