marmotta-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wik...@apache.org
Subject [50/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/
Date Tue, 19 Feb 2013 12:51:59 GMT
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/.project
----------------------------------------------------------------------
diff --git a/kiwi/.project b/kiwi/.project
new file mode 100644
index 0000000..7adbfab
--- /dev/null
+++ b/kiwi/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>kiwi-parent</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/.settings/org.eclipse.m2e.core.prefs
----------------------------------------------------------------------
diff --git a/kiwi/.settings/org.eclipse.m2e.core.prefs b/kiwi/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/kiwi/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/README.txt
----------------------------------------------------------------------
diff --git a/kiwi/README.txt b/kiwi/README.txt
new file mode 100644
index 0000000..f77f8ef
--- /dev/null
+++ b/kiwi/README.txt
@@ -0,0 +1,2 @@
+This will be the place where the custom LMF/KiWi triplestore implementation should be located in the future.
+See issue 107 (http://code.google.com/p/lmf/issues/detail?id=107)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-contextaware/pom.xml
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-contextaware/pom.xml b/kiwi/kiwi-contextaware/pom.xml
new file mode 100644
index 0000000..36b91c8
--- /dev/null
+++ b/kiwi/kiwi-contextaware/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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>
+
+    <parent>
+        <groupId>at.newmedialab.lmf</groupId>
+        <artifactId>kiwi-parent</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+
+    <artifactId>kiwi-contextaware</artifactId>
+    <packaging>jar</packaging>
+
+    <name>KiWi Triplestore: Contextual Sail</name>
+
+    <description>
+        This module provides a Sesame SailWrapper that allows defining a context that overwrites all
+        contexts defined in statements to be added, read, or deleted. It can be used to have a view
+        on repositories that only allows accessing a single named graph.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-model</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-sail-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSail.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSail.java b/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSail.java
new file mode 100644
index 0000000..f38f1f6
--- /dev/null
+++ b/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSail.java
@@ -0,0 +1,45 @@
+/**
+ * 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.contextaware;
+
+import org.openrdf.model.Resource;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailConnection;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+/**
+ * A SAIL wrapper that allows overriding any context given in repository operations
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ContextAwareSail extends SailWrapper {
+
+    private Resource context;
+
+    /**
+     * Creates a new SailWrapper that wraps the supplied Sail.
+     */
+    public ContextAwareSail(Sail baseSail, Resource context) {
+        super(baseSail);
+        this.context = context;
+    }
+
+    @Override
+    public SailConnection getConnection() throws SailException {
+        return new ContextAwareSailConnection(super.getConnection(),context);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSailConnection.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSailConnection.java b/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSailConnection.java
new file mode 100644
index 0000000..bebe527
--- /dev/null
+++ b/kiwi/kiwi-contextaware/src/main/java/org/apache/marmotta/kiwi/contextaware/ContextAwareSailConnection.java
@@ -0,0 +1,61 @@
+/**
+ * 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.contextaware;
+
+import info.aduna.iteration.CloseableIteration;
+import info.aduna.iteration.SingletonIteration;
+import org.openrdf.model.Resource;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.sail.SailConnection;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailConnectionWrapper;
+
+/**
+ * A SAIL connection overriding all provided context values with the default context used when creating the connection
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ContextAwareSailConnection extends SailConnectionWrapper {
+
+    private Resource context;
+
+    public ContextAwareSailConnection(SailConnection wrappedCon, Resource context) {
+        super(wrappedCon);
+        this.context = context;
+    }
+
+    @Override
+    public CloseableIteration<? extends Statement, SailException> getStatements(Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts) throws SailException {
+        return super.getStatements(subj, pred, obj, includeInferred, context);
+    }
+
+    @Override
+    public void addStatement(Resource subj, URI pred, Value obj, Resource... contexts) throws SailException {
+        super.addStatement(subj, pred, obj, context);
+    }
+
+    @Override
+    public CloseableIteration<? extends Resource, SailException> getContextIDs() throws SailException {
+        return new SingletonIteration<Resource, SailException>(context);
+    }
+
+    @Override
+    public void clear(Resource... contexts) throws SailException {
+        super.clear(context);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/pom.xml
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/pom.xml b/kiwi/kiwi-reasoner/pom.xml
new file mode 100644
index 0000000..6715190
--- /dev/null
+++ b/kiwi/kiwi-reasoner/pom.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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>
+
+    <parent>
+        <groupId>at.newmedialab.lmf</groupId>
+        <artifactId>kiwi-parent</artifactId>
+        <version>3.0.0-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+
+    <artifactId>kiwi-reasoner</artifactId>
+    <name>KiWi Triplestore: Reasoner</name>
+
+    <description>
+        This module implements the sKWRL rule-based reasoner for the KiWi triplestore. It allows defining custom
+        rule sets over RDF data. sKWRL is a purely monotonic reasoner without negation and implemented using
+        forward chaining on the database.
+    </description>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>javacc-maven-plugin</artifactId>
+                <version>2.6</version>
+                <executions>
+                    <execution>
+                        <id>javacc</id>
+                        <goals>
+                            <goal>javacc</goal>
+                        </goals>
+                        <configuration>
+                            <lookAhead>2147483647</lookAhead>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.7</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.directory}/generated-sources/javacc/</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>at.newmedialab.lmf</groupId>
+            <artifactId>kiwi-triplestore</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>at.newmedialab.lmf</groupId>
+            <artifactId>kiwi-transactions</artifactId>
+            </dependency>
+
+        <!-- Logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+
+        <!-- Sesame dependencies -->
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-sail-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-sail-inferencer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-model</artifactId>
+        </dependency>
+
+        <!-- Utilities -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>at.newmedialab.sesame</groupId>
+            <artifactId>sesame-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+
+        <!-- Testing -->
+        <dependency>
+            <groupId>at.newmedialab.lmf</groupId>
+            <artifactId>kiwi-triplestore</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <artifactId>junit</artifactId>
+            <groupId>junit</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency> <!-- see http://www.dbunit.org/howto.html -->
+            <artifactId>dbunit</artifactId>
+            <groupId>org.dbunit</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <artifactId>hamcrest-core</artifactId>
+            <groupId>org.hamcrest</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <artifactId>hamcrest-library</artifactId>
+            <groupId>org.hamcrest</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-sail-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-rio-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-rio-turtle</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-queryparser-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openrdf.sesame</groupId>
+            <artifactId>sesame-queryparser-sparql</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningConfiguration.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningConfiguration.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningConfiguration.java
new file mode 100644
index 0000000..b48fad2
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningConfiguration.java
@@ -0,0 +1,59 @@
+/**
+ * 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.engine;
+
+/**
+ * Hold the configuration of the reasoning engine.
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ReasoningConfiguration {
+
+    /**
+     * Let the reasoner commit a sail transaction (for added triples) after so many triple additions.
+     */
+    private int batchSize = 1000;
+
+    /**
+     * Eliminate duplicate justifications; can require additional time but reduces database overhead
+     */
+    private boolean removeDuplicateJustifications = true;
+
+    /**
+     * Number of parallel workers for processing reasoning rules.
+     */
+    private int workers = 4;
+
+    public ReasoningConfiguration() {
+    }
+
+    public int getBatchSize() {
+        return batchSize;
+    }
+
+    public void setBatchSize(int batchSize) {
+        this.batchSize = batchSize;
+    }
+
+    public boolean isRemoveDuplicateJustifications() {
+        return removeDuplicateJustifications;
+    }
+
+    public void setRemoveDuplicateJustifications(boolean removeDuplicateJustifications) {
+        this.removeDuplicateJustifications = removeDuplicateJustifications;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
new file mode 100644
index 0000000..514552b
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
@@ -0,0 +1,1027 @@
+/**
+ * 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.engine;
+
+import info.aduna.iteration.CloseableIteration;
+import info.aduna.iteration.EmptyIteration;
+import info.aduna.iteration.Iterations;
+import info.aduna.iteration.SingletonIteration;
+import org.apache.marmotta.kiwi.model.caching.TripleTable;
+import org.apache.marmotta.kiwi.model.rdf.KiWiNode;
+import org.apache.marmotta.kiwi.model.rdf.KiWiResource;
+import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
+import org.apache.marmotta.kiwi.model.rdf.KiWiUriResource;
+import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
+import org.apache.marmotta.kiwi.reasoner.model.program.LiteralField;
+import org.apache.marmotta.kiwi.reasoner.model.program.Pattern;
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.model.program.ResourceField;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.model.program.VariableField;
+import org.apache.marmotta.kiwi.reasoner.model.query.QueryResult;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
+import org.apache.marmotta.kiwi.sail.KiWiSailConnection;
+import org.apache.marmotta.kiwi.transactions.api.TransactionListener;
+import org.apache.marmotta.kiwi.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.model.TransactionData;
+import org.openrdf.model.Resource;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.sail.SailConnection;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailConnectionWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * This class implements the evaluation of reasoning programs on the triple store. It has two different modes:
+ * <ul>
+ * <li>incremental reasoning: when new triples are added or old triples are removed, the inferred information is updated accordingly</li>
+ * <li>full reasoning: (re-)runs the reasoning process over all triples currently contained in the triple store</li>
+ * </ul>
+ * Since reasoning can require some time, the actual execution of incremental reasonong is implemented in a
+ * producer-consumer style. When new transaction data is available, it is added to a queue. A separate reasoning
+ * thread then takes new transaction data and processes the rules asynchronously.
+ * <p/>
+ * The reasoning engine uses its own connection to the database to carry out reasoning tasks.
+ * <p/>
+ * TODO: we need to clarify conceptually whether it would be correct to run several reasoner threads in parallel.
+ * In theory, reasoning here is strictly monotonic so there should not be a problem. In practice, we might miss
+ * certain triples because the order of transactions might be messed up. Maybe it makes more sense to parallelize
+ * the execution or rules instead.
+ * <p/>
+ * User: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ReasoningEngine implements TransactionListener {
+
+    private static Logger log = LoggerFactory.getLogger(ReasoningEngine.class);
+
+    private static final String TASK_GROUP = "Reasoner";
+
+    /**
+     * A queue of transaction data objects of committed transactions, will be consumed by the reasoner
+     * thread in incremental reasoning.
+     */
+    private LinkedBlockingQueue<TransactionData> reasoningQueue;
+
+
+    /**
+     * A direct connection to the database to perform queries and store program-related information
+     */
+    private KiWiReasoningPersistence persistence;
+
+
+    /**
+     * A connection to the underlying KiWiStore to store new inferred triples and to access the value
+     * factory.
+     */
+    private TransactionalSail        store;
+
+    /**
+     * Configuration for this reasoning engine.
+     */
+    private ReasoningConfiguration   config;
+
+    /**
+     * In-memory cache of the currently active reasoning programs, (re-)initialized by {@link #loadPrograms()}.
+     */
+    private List<Program> programs;
+
+    /**
+     * In-memory cache to store all patterns that are candidates for matching triples and accessing the rules
+     * they belong to.
+     */
+    private Map<Pattern,Rule> patternRuleMap;
+
+    /**
+     * Internal counter to count executions of the reasoner (informational purposes only)
+     */
+    private static long taskCounter = 0;
+
+
+    /**
+     * The worker thread for the reasoner.
+     */
+    private SKWRLReasoner reasonerThread;
+
+    /**
+     * A lock to ensure that only once thread at a time is carrying out persistence
+     */
+    private Lock persistenceLock;
+
+    public ReasoningEngine(KiWiReasoningPersistence persistence, TransactionalSail store, ReasoningConfiguration config) {
+        this.persistence = persistence;
+        this.store = store;
+        this.config = config;
+        this.persistenceLock = new ReentrantLock();
+
+        loadPrograms();
+
+        this.reasoningQueue = new LinkedBlockingQueue<TransactionData>();
+        this.reasonerThread = new SKWRLReasoner();
+    }
+
+    public void loadPrograms() {
+        log.info("program configuration changed, reloading ...");
+        patternRuleMap = new HashMap<Pattern, Rule>();
+
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                programs       = Iterations.asList(connection.listPrograms());
+
+                for(Program p : programs) {
+                    for(Rule rule : p.getRules()) {
+                        for(Pattern pattern : rule.getBody()) {
+                            patternRuleMap.put(pattern,rule);
+                        }
+                    }
+                }
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            programs = Collections.emptyList();
+            log.warn("cannot load reasoning programs, reasoning disabled (error message: {})", ex.getMessage());
+        }
+    }
+
+
+    public void programChanged(Program program) {
+    }
+
+    /**
+     * In case a new rule has been added to one of the reasoning programs, process only this rule addition
+     * incrementally. Since the reasoner is strictly monotonic, this is sufficient.
+     *
+     * @param rule
+     */
+    public void notifyAddRule(Rule rule) {
+        startTask("Addition of rule " + rule.getName(), TASK_GROUP);
+
+        log.info("processing new rule: {}", rule);
+
+        try {
+            updateTaskStatus("processing new rule ...");
+            processRule(rule, null, null);
+
+        } catch(Exception ex) {
+            log.error("error while processing rule",ex);
+
+            return;
+        } finally {
+            endTask();
+        }
+    }
+
+    /**
+     * In case an existing rule has been removed from one of the reasoning programs, remove all triples
+     * that are based on this rule by consulting the justifications that use this rule. Since the reasoner
+     * is strictly monotonic, this is sufficient to get the correct set of materialized triples.
+     *
+     */
+    public void notifyRemoveRules() {
+        startTask("Removing Rules", TASK_GROUP);
+
+        // clean up justifications depending on the rule
+        updateTaskStatus("cleaning up unsupported triples");
+
+
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+
+                // this is done automatically now when updating or deleting a program:
+                // removeJustificationsByQuery("reasoner.listJustificationsByRule", ImmutableMap.of("rule", (Object) rule));
+
+                // then remove all inferred triples that are no longer supported
+                cleanupUnsupported(connection);
+
+                // and finally garbage collect those triples that are inferred and deleted
+                // garbage collection is now carried out by a thread in the triple store
+                //garbageCollectTriples();
+            } catch (SQLException ex) {
+                connection.rollback();
+                throw ex;
+            } finally {
+                connection.close();
+            }
+        } catch (SailException ex) {
+            log.error("REPOSITORY ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
+            log.debug("Exception details:", ex);
+        } catch (SQLException ex) {
+            log.error("DATABASE ERROR: could not clean up justifications for triples, database state will be inconsistent! Message: {}", ex.getMessage());
+            log.debug("Exception details:", ex);
+        }
+
+
+        endTask();
+    }
+
+
+
+    /**
+     * Incrementally apply the updates that are contained in the transaction
+     * data to the reasoning programs that are in the system.
+     * <p/>
+     * Called after a transaction has committed. The transaction data will contain all changes done in the transaction since
+     * the last commit. This method should be used in case the transaction listener aims to perform additional activities
+     * in a new transaction or outside the transaction management, e.g. notifying a server on the network, adding
+     * data to a cache, or similar.
+     *
+     * @param data
+     */
+    @Override
+    public void afterCommit(TransactionData data) {
+        if( (data.getAddedTriples().size() > 0 || data.getRemovedTriples().size() > 0) && patternRuleMap.size() > 0) {
+
+            reasoningQueue.remove(data);
+            if (!reasoningQueue.offer(data)) {
+                log.info("waiting for reasoning queue to become available ...");
+                try {
+                    reasoningQueue.put(data);
+                    log.info("reasoning queue available, added data");
+                } catch (InterruptedException e) {
+                    log.error("interrupted while waiting for reasoning queue to become available ...");
+                }
+            };
+
+
+        }
+    }
+
+    /**
+     * Called before a transaction commits. The transaction data will contain all changes done in the transaction since
+     * the last commit. This method should be used in case the transaction listener aims to perform additional activities
+     * in the same transaction, like inserting or updating database tables.
+     *
+     * @param data
+     */
+    @Override
+    public void beforeCommit(TransactionData data) {
+        // do nothing
+    }
+
+    /**
+     * Called when a transaction rolls back.
+     */
+    @Override
+    public void rollback(TransactionData data) {
+        // do nothing
+    }
+
+
+    /**
+     * Start a new reasoner task to collect status messages. Informational purposes only.
+     * @param name
+     */
+    protected void startTask(String name, String taskGroup) {
+        // if a task is already running, this should create a nested subtask
+    }
+
+    /**
+     * Stop the currently active reasoning task. Informational purposes only.
+     * @param name
+     */
+    protected void endTask() {
+
+    }
+
+
+    /**
+     * Update the status of the reasoner task with the given message. This will simply send all update messages to
+     * registered status listeners.
+     * @param message
+     */
+    protected void updateTaskStatus(String message) {
+
+    }
+
+    /**
+     * Update the task progress (if any)
+     * @param progress
+     */
+    protected void updateTaskProgress(int progress) {
+
+    }
+
+    /**
+     * Set the maximum steps needed in the task progress (if any)
+     * @param progress
+     */
+    protected void updateTaskMaxProgress(int progress) {
+
+    }
+
+    private void executeReasoner(TransactionData data) {
+        updateTaskStatus("fetching worklist");
+        Set<KiWiTriple> newTriples = new HashSet<KiWiTriple>();
+        for(Statement stmt : data.getAddedTriples()) {
+            KiWiTriple t = (KiWiTriple)stmt;
+            if(t.isMarkedForReasoning()) {
+                newTriples.add(t);
+                t.setMarkedForReasoning(false);
+            }
+        }
+
+        //taskManagerService.setTaskSteps(newTriples.size() + data.getRemovedTriples().size());
+        // evaluate the rules for all added triples
+        if(newTriples.size() > 0) {
+            long start2 = System.currentTimeMillis();
+            updateTaskStatus("reasoning over " + newTriples.size() + " new triples");
+            processRules(newTriples);
+            log.info("REASONER: reasoning for {} new triples took {} ms overall",newTriples.size(),System.currentTimeMillis()-start2);
+        }
+
+        if(data.getRemovedTriples().size() > 0) {
+            log.debug("cleaning up justifications and inferences for {} triples",data.getRemovedTriples().size());
+            try {
+                KiWiReasoningConnection connection = persistence.getConnection();
+                try {
+                    // first clean up justifications that are no longer supported
+                    cleanupJustifications(connection, data.getRemovedTriples());
+
+
+                    // then remove all inferred triples that are no longer supported
+                    cleanupUnsupported(connection);
+
+                    // and finally garbage collect those triples that are inferred and deleted
+                    // garbage collection is now carried out by a thread in the triple store
+                    //garbageCollectTriples();
+                    connection.commit();
+                } catch (SQLException ex) {
+                    connection.rollback();
+                    throw ex;
+                } finally {
+                    connection.close();
+                }
+            } catch (SailException ex) {
+                log.error("REPOSITORY ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
+                log.debug("Exception details:", ex);
+            } catch (SQLException ex) {
+                log.error("DATABASE ERROR: could not clean up justifications for triples, database state will be inconsistent! Message: {}", ex.getMessage());
+                log.debug("Exception details:", ex);
+            }
+
+
+        }
+
+    }
+
+    /**
+     * Clean all inferred triples and re-run all reasoning rules.
+     */
+    public void reRunPrograms() {
+        final String taskName = "Reasoner Task "+ ++taskCounter + " (full reasoning)";
+
+        startTask("Synchronous " + taskName, TASK_GROUP);
+        executeReasoner();
+        endTask();
+    }
+
+
+    /**
+     * Perform a full reasoning over the triples and rules contained in the database. Will first remove all existing
+     * inferred triples and justifications and then evaluate each of the rules in turn.
+     */
+    private void executeReasoner() {
+        // clean up all justifications
+        updateTaskStatus("removing old justifications");
+
+
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+
+                // remove all justifications from the database
+                connection.deleteJustifications();
+
+                // clean up inferred triples by removing them from the triple store; the transaction system should take care of the rest
+                cleanupUnsupported(connection);
+
+                // and finally garbage collect those triples that are inferred and deleted
+                // garbage collection is now carried out by a thread in the triple store
+                //garbageCollectTriples();
+                connection.commit();
+            } catch (SQLException ex) {
+                connection.rollback();
+                throw ex;
+            } finally {
+                connection.close();
+            }
+        } catch (SailException ex) {
+            log.error("REPOSITORY ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
+            log.debug("Exception details:", ex);
+        } catch (SQLException ex) {
+            log.error("DATABASE ERROR: could not clean up justifications for triples, database state will be inconsistent! Message: {}", ex.getMessage());
+            log.debug("Exception details:", ex);
+        }
+
+        // process the rules over the whole dataset
+        try {
+            updateTaskStatus("processing rules ...");
+
+            //Set<Callable<Boolean>> tasks = new HashSet<Callable<Boolean>>();
+            for(Program p : programs) {
+                for(final Rule rule : p.getRules()) {
+                    startTask("Rule Processing", TASK_GROUP);
+                    updateTaskStatus("processing rule " + rule.getName() + " ...");
+                    processRule(rule, null, null);
+                    endTask();
+                    // TODO: multithreading is currently not working reliably (some nodes might get inserted twice)
+
+/*
+                    tasks.add(new Callable<Boolean>() {
+                        @Override
+                        public Boolean call() throws Exception {
+                            startTask("Rule Processing", TASK_GROUP);
+                            updateTaskStatus("processing rule " + rule.getName() + " ...");
+                            processRule(rule, null, null);
+                            endTask();
+                            return Boolean.TRUE;
+                        }
+                    });
+*/
+                }
+            }
+            //workers.invokeAll(tasks);
+        } catch(Exception ex) {
+            log.error("error while processing rules", ex);
+
+            return;
+        }
+
+    }
+
+    /**
+     * Store new justifications in the database. The method performs a batched operation to avoid
+     * excessive resource use.
+     *
+     * @param justifications
+     */
+    private void storeJustifications(Iterable<Justification> justifications) {
+        // persist the justifications that have been created in the rule processing
+        long counter = 0;
+        updateTaskStatus("storing new justifications ...");
+
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                connection.storeJustifications(justifications);
+                connection.commit();
+            } catch (SQLException ex) {
+                connection.rollback();
+                throw ex;
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            log.error("DATABASE ERROR: could not add new justifications for triples, database state will be inconsistent! Message: {}",ex.getMessage());
+            log.debug("Exception details:",ex);
+        }
+    }
+
+
+    /**
+     * This method iterates over all triples that are passed as argument and
+     * checks whether they are used as supporting triples justifications. All
+     * such justifications are removed. Triples that are no longer supported
+     * will later be cleaned up by {@link #cleanupUnsupported()}
+     *
+     * @param removedTriples
+     */
+    private void cleanupJustifications(KiWiReasoningConnection connection, TripleTable<Statement> removedTriples) throws SQLException {
+        updateTaskStatus("cleaning up justifications for " + removedTriples.size() + " removed triples");
+        for(Statement stmt : removedTriples) {
+            KiWiTriple t = (KiWiTriple)stmt;
+            connection.deleteJustifications(t);
+        }
+    }
+
+
+    /**
+     * Cleanup inferred triples that are no longer supported by any justification.
+     */
+    private void cleanupUnsupported(KiWiReasoningConnection connection) throws SQLException, SailException {
+        updateTaskStatus("cleaning up unsupported triples");
+
+        int count = 0, total = 0;
+
+        startTask("Unsupported Triple Cleaner", TASK_GROUP);
+        updateTaskStatus("loading unsupported triples");
+
+        CloseableIteration<KiWiTriple,SQLException> tripleIterator = connection.listUnsupportedTriples();
+
+        updateTaskStatus("deleting unsupported triples");
+        SailConnection tc = store.getConnection();
+        KiWiSailConnection ic = getWrappedConnection(tc);
+        try {
+            tc.begin();
+            while(tripleIterator.hasNext()) {
+                ic.removeInferredStatement(tripleIterator.next());
+                count++;
+            }
+            log.debug("removed {} unsupported triples",count);
+            tc.commit();
+        } catch(SailException ex) {
+            ic.rollback();
+            throw ex;
+        } finally {
+            Iterations.closeCloseable(tripleIterator);
+            ic.close();
+        }
+    }
+
+
+    /**
+     *
+     * @param addedTriples
+     */
+    private void processRules(final Set<KiWiTriple> addedTriples) {
+        try {
+            updateTaskStatus("processing rules ...");
+            // select the rules that have at least one matching pattern; the match method will
+            // return a set of variable bindings that we will be used to prepopulate the bindings
+//            Set<Callable<Boolean>> tasks = new HashSet<Callable<Boolean>>();
+            for(final Pattern pattern : patternRuleMap.keySet()) {
+                for(KiWiTriple triple : addedTriples) {
+                    QueryResult match = matches(pattern,triple);
+                    if(match != null) {
+                        log.debug("pattern {} matched with triple {}", pattern.toString(), triple.toString());
+                        processRule(patternRuleMap.get(pattern), match, pattern);
+                    }
+                }
+                // TODO: for parallel reasoning, the problem is that we should only create one thread per rule and not
+                // one per pattern, otherwise we can get duplicates because the same rule is evaluated twice
+/*
+                tasks.add(new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws Exception {
+                        for(KiWiTriple triple : addedTriples) {
+                            QueryResult match = matches(pattern,triple);
+                            if(match != null) {
+                                log.debug("pattern {} matched with triple {}", pattern.toString(), triple.toString());
+                                processRule(patternRuleMap.get(pattern), match, pattern);
+                            }
+                        }
+
+                        return Boolean.TRUE;
+                    }
+                });
+*/
+            }
+            //workers.invokeAll(tasks);
+
+        } catch(Exception ex) {
+            log.error("error while processing rules",ex);
+        }
+    }
+
+    /**
+     * Process the rule given as argument. The set of bindings passed as argument is used as a seed of
+     * bindings and will be further populated by pattern matches. The set of justifications is passed over
+     * from previous calls so that justifications can be persisted in a batch.
+     *
+     * @param rule
+     * @param match
+     */
+    private void processRule(Rule rule, QueryResult match, Pattern p) {
+
+        // get the variable bindings for the rule evaluation
+        log.debug("REASONER: evaluating rule body for rule {} ...", rule);
+
+        // create a collection consisting of the body minus the pattern that already matched
+        Set<Pattern> body = new HashSet<Pattern>(rule.getBody());
+
+        if(p != null) {
+            body.remove(p);
+        }
+
+        CloseableIteration<QueryResult, SQLException> bodyResult;
+
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            SailConnection     sail = store.getConnection();
+            KiWiSailConnection isail = getWrappedConnection(sail);
+            try {
+
+                // if there are further patterns, evaluate them; if the matched pattern was the only pattern, then
+                // simply take the match as binding
+                if(body.size() > 0) {
+                    bodyResult = connection.query(body,match,null,null,true);
+                } else if(match != null) {
+                    bodyResult = new SingletonIteration<QueryResult, SQLException>(match);
+                } else {
+                    bodyResult = new EmptyIteration<QueryResult, SQLException>();
+                }
+
+                // construct triples out of the bindings and the rule heads
+                long counter = 0;
+
+                // initialise a new set of justifications
+                Set<Justification> justifications = new HashSet<Justification>();
+
+                sail.begin();
+                while(bodyResult.hasNext()) {
+                    QueryResult row = bodyResult.next();
+                    Map<VariableField,KiWiNode> binding = row.getBindings();
+
+                    Resource subject = null;
+                    URI property = null;
+                    Value object;
+
+                    if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isVariableField()) {
+                        if(!binding.get(rule.getHead().getSubject()).isUriResource() && !binding.get(rule.getHead().getSubject()).isAnonymousResource()) {
+                            log.info("cannot use value {} as subject, because it is not a resource",binding.get(rule.getHead().getSubject()));
+                            continue;
+                        }
+                        subject = (KiWiResource)binding.get(rule.getHead().getSubject());
+                    } else if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isResourceField()) {
+                        subject = ((ResourceField)rule.getHead().getSubject()).getResource();
+                    } else
+                        throw new IllegalArgumentException("Subject of rule head may only be a variable or a resource; rule: "+rule);
+
+                    if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isVariableField()) {
+                        if(!binding.get(rule.getHead().getProperty()).isUriResource()) {
+                            log.info("cannot use value {} as property, because it is not a URI resource",binding.get(rule.getHead().getProperty()));
+                            continue;
+                        }
+                        property = (KiWiUriResource)binding.get(rule.getHead().getProperty());
+                    } else if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isResourceField()) {
+                        property = (KiWiUriResource)((ResourceField)rule.getHead().getProperty()).getResource();
+                    } else
+                        throw new IllegalArgumentException("Property of rule head may only be a variable or a resource; rule: "+rule);
+
+                    if(rule.getHead().getObject() != null && rule.getHead().getObject().isVariableField()) {
+                        object = binding.get(rule.getHead().getObject());
+                    } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isResourceField()) {
+                        object = ((ResourceField)rule.getHead().getObject()).getResource();
+                    } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isLiteralField()) {
+                        object = ((LiteralField)rule.getHead().getObject()).getLiteral();
+                    } else
+                        throw new IllegalArgumentException("Object of rule head may only be a variable, a literal, or a resource; rule: "+rule);
+
+
+                    KiWiTriple triple = isail.addInferredStatement(subject, property, object);
+
+                    Justification justification = new Justification();
+                    justification.setTriple(triple);
+                    justification.getSupportingRules().add(rule);
+                    justification.getSupportingTriples().addAll(row.getJustifications());
+                    justifications.add(justification);
+
+                    // when the batch size is reached, commit the transaction, save the justifications, and start a new
+                    // transaction and new justification set
+                    if(++counter % config.getBatchSize() == 0) {
+                        persistenceLock.lock();
+
+                        try {
+
+                            sail.commit();
+
+                            log.debug("adding {} justifications",justifications.size());
+
+                            updateTaskStatus("storing justifications ...");
+                            Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
+
+                            if(config.isRemoveDuplicateJustifications()) {
+                                removeDuplicateJustifications(connection,baseJustifications);
+                            }
+
+                            log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
+
+                            // persist the justifications that have been created in the rule processing
+                            if(baseJustifications.size() > 0) {
+                                connection.storeJustifications(baseJustifications);
+                            }
+                            connection.commit();
+                            sail.begin();
+                        } finally {
+                            persistenceLock.unlock();
+                        }
+                        justifications.clear();
+                    }
+                }
+
+                persistenceLock.lock();
+                try {
+                    sail.commit();
+
+                    log.debug("adding {} justifications",justifications.size());
+                    updateTaskStatus("storing justifications ...");
+                    Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
+
+                    if(config.isRemoveDuplicateJustifications()) {
+                        removeDuplicateJustifications(connection,baseJustifications);
+                    }
+
+                    // persist the justifications that have been created in the rule processing
+                    if(baseJustifications.size() > 0) {
+                        connection.storeJustifications(baseJustifications);
+                    }
+
+                    log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
+
+                    Iterations.closeCloseable(bodyResult);
+                    connection.commit();
+                } finally {
+                    persistenceLock.unlock();
+                }
+            } catch(SailException ex) {
+                connection.rollback();
+                sail.rollback();
+                throw ex;
+            } catch(SQLException ex) {
+                sail.rollback();
+                connection.rollback();
+                throw ex;
+            } finally {
+                connection.close();
+                sail.close();
+            }
+
+        } catch(SQLException ex) {
+            log.error("DATABASE ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
+            log.debug("Exception details:",ex);
+        } catch (SailException ex) {
+            log.error("REPOSITORY ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
+            log.debug("Exception details:", ex);
+        }
+
+    }
+
+    /**
+     * Return the justifications for the triple passed as argument.
+     * @param t
+     * @return
+     */
+    private Collection<Justification> getJustifications(KiWiReasoningConnection connection, KiWiTriple t, Set<Justification> transactionJustifications) throws SQLException {
+        // TODO: transactionJustifications are ignored
+        HashSet<Justification> justifications = new HashSet<Justification>();
+        Iterations.addAll(connection.listJustificationsForTriple(t), justifications);
+        for(Justification j : transactionJustifications) {
+            if(j.getTriple().equals(t)) {
+                justifications.add(j);
+            }
+        }
+        return justifications;
+    }
+
+    /**
+     * For all justifications contained in the set passed as argument, create corresponding base justifications,
+     * i.e. justifications that only contain base triples and no inferred triples.
+     *
+     * @param justifications
+     * @return
+     */
+    private Set<Justification> getBaseJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException {
+        Set<Justification> baseJustifications = new HashSet<Justification>();
+        Map<KiWiTriple,Collection<Justification>> justificationCache = new HashMap<KiWiTriple, Collection<Justification>>();
+
+        for(Justification justification : justifications) {
+            KiWiTriple triple = justification.getTriple();
+
+            Justification newJustification = new Justification();
+            newJustification.setSupportingRules(justification.getSupportingRules());
+            newJustification.setTriple(triple);
+
+            Set<Justification> tripleJustifications = Collections.singleton(newJustification);
+
+            // resolve inferred triples by replacing them by their justifications
+            for(KiWiTriple support : justification.getSupportingTriples()) {
+                if(support.isInferred()) {
+                    Collection<Justification> supportJustifications = justificationCache.get(support);
+                    // cache justifications of triple in case they are needed again in this run
+                    if(supportJustifications == null || supportJustifications.size() == 0) {
+                        supportJustifications = getJustifications(connection, support, baseJustifications);
+                        justificationCache.put(support,supportJustifications);
+                    }
+
+                    if(supportJustifications.size() == 0) {
+                        log.error("error: inferred triple {} is not justified!",support);
+                    }
+
+                    // mix the two sets
+                    Set<Justification> oldTripleJustifications = tripleJustifications;
+                    tripleJustifications = new HashSet<Justification>();
+                    for(Justification j1 : oldTripleJustifications) {
+                        for(Justification j2 : supportJustifications) {
+                            Justification j3 = new Justification();
+                            j3.setTriple(triple);
+                            j3.getSupportingTriples().addAll(j1.getSupportingTriples());
+                            j3.getSupportingTriples().addAll(j2.getSupportingTriples());
+                            j3.getSupportingRules().addAll(j1.getSupportingRules());
+                            j3.getSupportingRules().addAll(j2.getSupportingRules());
+                            tripleJustifications.add(j3);
+                        }
+                    }
+                } else {
+                    for(Justification j : tripleJustifications) {
+                        j.getSupportingTriples().add(support);
+                    }
+                }
+            }
+
+            baseJustifications.addAll(tripleJustifications);
+        }
+        return baseJustifications;
+    }
+
+    /**
+     * The reasoner might create identical justifications in the process of generating the base justifications
+     * for a triple. This method removes duplicates at the expense of additional computation time.
+     * @param justifications
+     */
+    private void removeDuplicateJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException {
+        // remove duplicate justifications
+        HashMap<KiWiTriple,Collection<Justification>> justificationCache = new HashMap<KiWiTriple, Collection<Justification>>();
+        for(Iterator<Justification> it = justifications.iterator(); it.hasNext(); ) {
+            Justification j = it.next();
+
+            Collection<Justification> supportJustifications = justificationCache.get(j.getTriple());
+            // cache justifications of triple in case they are needed again in this run
+            if(supportJustifications == null) {
+                supportJustifications = getJustifications(connection, j.getTriple(), Collections.<Justification>emptySet());
+                justificationCache.put(j.getTriple(),supportJustifications);
+            }
+
+            if(supportJustifications.contains(j)) {
+                it.remove();
+            }
+        }
+    }
+
+
+    private QueryResult matches(Pattern pattern, KiWiTriple triple) {
+        boolean result = true;
+
+        QueryResult match = new QueryResult();
+
+        if(pattern.getSubject().isResourceField()) {
+            result = ((ResourceField)pattern.getSubject()).getResource().equals(triple.getSubject());
+        } else if(pattern.getSubject().isVariableField()) {
+            KiWiNode binding = match.getBindings().get(pattern.getSubject());
+            if(binding != null) {
+                result = binding.equals(triple.getSubject());
+            } else {
+                match.getBindings().put((VariableField) pattern.getSubject(), triple.getSubject());
+            }
+        }
+
+        if(result && pattern.getProperty().isResourceField()) {
+            result = ((ResourceField)pattern.getProperty()).getResource().equals(triple.getPredicate());
+        } else if(result && pattern.getProperty().isVariableField()) {
+            KiWiNode binding = match.getBindings().get(pattern.getProperty());
+            if(binding != null) {
+                result = binding.equals(triple.getPredicate());
+            } else {
+                match.getBindings().put((VariableField) pattern.getProperty(), triple.getPredicate());
+            }
+        }
+
+        if(result && pattern.getContext() != null && pattern.getContext().isResourceField()) {
+            result = ((ResourceField)pattern.getContext()).getResource().equals(triple.getContext());
+        } else if(result && pattern.getContext() != null && pattern.getContext().isVariableField()) {
+            KiWiNode binding = match.getBindings().get(pattern.getContext());
+            if(binding != null) {
+                result = binding.equals(triple.getContext());
+            } else {
+                match.getBindings().put((VariableField) pattern.getContext(), triple.getContext());
+            }
+        }
+
+        if(result && pattern.getObject().isResourceField()) {
+            result = ((ResourceField)pattern.getObject()).getResource().equals(triple.getObject());
+        } else if(result && pattern.getObject().isLiteralField()) {
+            result = ((LiteralField)pattern.getObject()).getLiteral().equals(triple.getObject());
+        } else if(result && pattern.getObject().isVariableField()) {
+            KiWiNode binding = match.getBindings().get(pattern.getObject());
+            if(binding != null) {
+                result = binding.equals(triple.getObject());
+            } else {
+                match.getBindings().put((VariableField) pattern.getObject(), triple.getObject());
+            }
+        }
+
+
+        if(result) {
+            match.getJustifications().add(triple);
+            return match;
+        } else
+            return null;
+    }
+
+    /**
+     * Return true in case the reasoner is currently executing, false otherwise.
+     * @return
+     */
+    public boolean isRunning() {
+        return reasonerThread.isRunning() || !reasoningQueue.isEmpty();
+    }
+
+    public void shutdown() {
+        log.info("shutting down reasoning service ...");
+        reasonerThread.shutdown();
+    }
+
+    /**
+     * Return the KiWiSailConnection underlying a given sail connection. The method will follow wrapped
+     * connections until it finds the KiWiSailConnection, or otherwise throws a SailException.
+     * @param connection
+     * @return
+     */
+    private KiWiSailConnection getWrappedConnection(SailConnection connection) throws SailException {
+        SailConnection it = connection;
+        while(it instanceof SailConnectionWrapper) {
+            it = ((SailConnectionWrapper) it).getWrappedConnection();
+            if(it instanceof KiWiSailConnection) {
+                return (KiWiSailConnection) it;
+            }
+        }
+        throw new SailException("no underlying KiWiSailConnection found for connection");
+    }
+
+    private static int indexerCounter = 0;
+
+    private class SKWRLReasoner extends Thread {
+        private boolean shutdown = false;
+        private boolean running  = false;
+
+        private SKWRLReasoner() {
+            super("SKWRL Reasoner " + ++indexerCounter);
+            setDaemon(true);
+            start();
+        }
+
+        public void shutdown() {
+            shutdown = true;
+            this.interrupt();
+        }
+
+        public boolean isRunning() {
+            return running;
+        }
+
+        @Override
+        public void run() {
+            log.info("{} starting up ...", getName());
+
+            startTask(getName(), TASK_GROUP);
+
+            while (!shutdown || reasoningQueue.size() > 0) {
+                running = false;
+                try {
+                    updateTaskStatus("idle");
+
+                    TransactionData data = reasoningQueue.take();
+                    running = true;
+
+                    updateTaskMaxProgress(reasoningQueue.size());
+
+                    executeReasoner(data);
+                } catch (InterruptedException ex) {
+
+                } catch (Exception ex) {
+                    log.warn("reasoning task threw an exception",ex);
+                }
+            }
+            try {
+                endTask();
+            } catch (Exception ex) {
+            }
+            log.info("{} shutting down ...", getName());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Field.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Field.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Field.java
new file mode 100644
index 0000000..c96ac69
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Field.java
@@ -0,0 +1,39 @@
+/**
+ * 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.model.program;
+
+import java.util.Map;
+
+/**
+ * Add file description here!
+ * <p/>
+ * User: sschaffe
+ */
+public interface Field {
+
+    public boolean isResourceField();
+
+    public boolean isLiteralField();
+
+    public boolean isVariableField();
+
+    /**
+     * Create string representation taking into account the namespace definitions given as argument.
+     * @param namespaces
+     * @return
+     */
+    public String toString(Map<String,String> namespaces);
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Filter.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Filter.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Filter.java
new file mode 100644
index 0000000..8f01616
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Filter.java
@@ -0,0 +1,28 @@
+/**
+ * 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.model.program;
+
+/**
+ * Base class for query filters. Query filters are used to further refine the query results, e.g.
+ * by adding a condition on bindings of a variable (AGE < 35).
+ * <p/>
+ * Currently, the LMF does not evaluate any filters.
+ * <p/>
+ * User: sschaffe
+ */
+public interface Filter {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
new file mode 100644
index 0000000..a5b1876
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
@@ -0,0 +1,141 @@
+/**
+ * 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.model.program;
+
+
+import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Reason Maintenance: A justification for the existence of a triple. The justification
+ * contains a reference to the triple it supports and references to the triples and
+ * rules that support this justification.
+ * <p/>
+ * A justification is created and stored each time a reasoning rule infers new triples.
+ * Each of the inferred triples will be supported by the rules and base triples that
+ * were used in the inference. This makes both explanations and maintenance much simpler,
+ * i.e. when a base triple is deleted, we can remove all justifications that contain it
+ * and then remove all inferred triples that are no longer supported by any justification.
+ *
+ * <p/>
+ * User: sschaffe
+ */
+public class Justification  {
+
+    private Long id;
+
+    /**
+     * The triple that is supported by this justification
+     */
+    private KiWiTriple triple;
+
+    /**
+     * The base triples that support this justification
+     */
+    private Set<KiWiTriple> supportingTriples;
+
+
+    /**
+     * The rules that support this justification
+     */
+    private Set<Rule> supportingRules;
+
+    /**
+     * The date when the justification was created (stored in the database)
+     */
+    private Date createdAt;
+
+    public Justification() {
+        supportingTriples = new HashSet<KiWiTriple>();
+        supportingRules   = new HashSet<Rule>();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public KiWiTriple getTriple() {
+        return triple;
+    }
+
+    public void setTriple(KiWiTriple triple) {
+        this.triple = triple;
+    }
+
+    public Set<KiWiTriple> getSupportingTriples() {
+        return supportingTriples;
+    }
+
+    public void setSupportingTriples(Set<KiWiTriple> supportingTriples) {
+        this.supportingTriples = supportingTriples;
+    }
+
+    public Set<Rule> getSupportingRules() {
+        return supportingRules;
+    }
+
+    public void setSupportingRules(Set<Rule> supportingRules) {
+        this.supportingRules = supportingRules;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Justification that = (Justification) o;
+
+        //if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (!supportingRules.equals(that.supportingRules)) return false;
+        if (!supportingTriples.equals(that.supportingTriples)) return false;
+        if (!triple.equals(that.triple)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 0; // id != null ? id.hashCode() : 0;
+        result = 31 * result + triple.hashCode();
+        result = 31 * result + supportingTriples.hashCode();
+        result = 31 * result + supportingRules.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "Justification{" +
+                "triple=" + triple +
+                ", supportingTriples=" + supportingTriples +
+                ", supportingRules=" + supportingRules +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/LiteralField.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/LiteralField.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/LiteralField.java
new file mode 100644
index 0000000..0d99762
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/LiteralField.java
@@ -0,0 +1,94 @@
+/**
+ * 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.model.program;
+
+import org.apache.marmotta.kiwi.model.rdf.KiWiLiteral;
+import org.openrdf.model.Literal;
+
+import java.util.Map;
+
+/**
+ * Add file description here!
+ * <p/>
+ * User: sschaffe
+ */
+public class LiteralField implements Field {
+
+    private Literal literal;
+
+    public LiteralField() {
+    }
+
+    public LiteralField(Literal literal) {
+        this.literal = literal;
+    }
+
+    public Literal getLiteral() {
+        return literal;
+    }
+
+    public void setLiteral(KiWiLiteral literal) {
+        this.literal = literal;
+    }
+
+    public String toString() {
+        return "\"" + literal.getLabel() + "\"";
+    }
+
+
+    /**
+     * Create string representation taking into account the namespace definitions given as argument.
+     *
+     * @param namespaces
+     * @return
+     */
+    @Override
+    public String toString(Map<String, String> namespaces) {
+        return "\"" + literal.getLabel() + "\"";
+    }
+
+    @Override
+    public boolean isResourceField() {
+        return false;
+    }
+
+    @Override
+    public boolean isLiteralField() {
+        return true;
+    }
+
+    @Override
+    public boolean isVariableField() {
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        LiteralField that = (LiteralField) o;
+
+        if (literal != null ? !literal.equals(that.literal) : that.literal != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return literal != null ? literal.hashCode() : 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
new file mode 100644
index 0000000..c72b8c9
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
@@ -0,0 +1,118 @@
+/**
+ * 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.model.program;
+
+import java.util.Map;
+
+/**
+ * Representation of a graph pattern. A graph pattern has a subject, predicate, and object, and
+ * optionally a context. Subject, predicate and object may be either RDF nodes or variables or null.
+ * Context is either null or a RDF resource.
+ *
+ *
+ * User: sschaffe
+ */
+public class Pattern  {
+
+
+    private Field subject;
+
+    private Field property;
+
+    private Field object;
+
+    private Field context;
+
+    public Pattern() {
+    }
+
+
+    public Pattern(Field subject, Field property, Field object) {
+        this.setSubject(subject);
+        this.setProperty(property);
+        this.setObject(object);
+    }
+
+
+    public Field getSubject() {
+        return subject;
+    }
+
+    public void setSubject(Field subject) {
+        this.subject = subject;
+    }
+
+    public Field getProperty() {
+        return property;
+    }
+
+    public void setProperty(Field property) {
+        this.property = property;
+    }
+
+    public Field getObject() {
+        return object;
+    }
+
+    public void setObject(Field object) {
+        this.object = object;
+    }
+
+    public Field getContext() {
+        return context;
+    }
+
+    public void setContext(Field context) {
+        this.context = context;
+    }
+
+    public String toString() {
+        return "(" + getSubject().toString() + " " + getProperty().toString() + " " + getObject().toString() + ")";
+    }
+
+    public String toString(Map<String,String> namespaces) {
+        return "(" + getSubject().toString(namespaces) + " " + getProperty().toString(namespaces) + " " + getObject().toString(namespaces) + ")";
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        if(context == null && object == null && subject == null && property == null) {
+            return super.equals(o);
+        }
+
+        Pattern pattern = (Pattern) o;
+
+        if (context != null ? !context.equals(pattern.context) : pattern.context != null) return false;
+        if (object != null ? !object.equals(pattern.object) : pattern.object != null) return false;
+        if (property != null ? !property.equals(pattern.property) : pattern.property != null) return false;
+        if (subject != null ? !subject.equals(pattern.subject) : pattern.subject != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = subject != null ? subject.hashCode() : 0;
+        result = 31 * result + (property != null ? property.hashCode() : 0);
+        result = 31 * result + (object != null ? object.hashCode() : 0);
+        result = 31 * result + (context != null ? context.hashCode() : 0);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Program.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Program.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Program.java
new file mode 100644
index 0000000..7c199c8
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Program.java
@@ -0,0 +1,143 @@
+/**
+ * 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.model.program;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Add file description here!
+ * <p/>
+ * User: sschaffe
+ */
+public class Program {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    private Map<String,String> namespaces;
+
+    private List<Rule> rules;
+
+
+    public Program() {
+        rules = new ArrayList<Rule>();
+        namespaces = new HashMap<String, String>();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Map<String, String> getNamespaces() {
+        return namespaces;
+    }
+
+    public void setNamespaces(Map<String, String> namespaces) {
+        this.namespaces = namespaces;
+    }
+
+    public void addNamespace(String prefix, String uri) {
+        getNamespaces().put(prefix,uri);
+    }
+
+
+    public List<Rule> getRules() {
+        return rules;
+    }
+
+    public void setRules(List<Rule> rules) {
+        this.rules = rules;
+    }
+
+
+    public void addRule(Rule rule) {
+        getRules().add(rule);
+    }
+
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (Map.Entry<String, String> ns : getNamespaces().entrySet()) {
+            builder.append("@prefix ");
+            builder.append(ns.getKey());
+            builder.append(": <");
+            builder.append(ns.getValue());
+            builder.append(">\n");
+        }
+        if(getNamespaces().size() > 0) {
+            builder.append("\n");
+        }
+
+        for(Rule rule : getRules()) {
+            builder.append(rule.toString(getNamespaces()));
+            builder.append("\n");
+
+        }
+        return builder.toString();
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Program program = (Program) o;
+
+        if (description != null ? !description.equals(program.description) : program.description != null) return false;
+        if (!name.equals(program.name)) return false;
+        if (!namespaces.equals(program.namespaces)) return false;
+        if (!rules.equals(program.rules)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + (description != null ? description.hashCode() : 0);
+        result = 31 * result + namespaces.hashCode();
+        result = 31 * result + rules.hashCode();
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/ResourceField.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/ResourceField.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/ResourceField.java
new file mode 100644
index 0000000..c523581
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/ResourceField.java
@@ -0,0 +1,107 @@
+/**
+ * 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.model.program;
+
+import org.openrdf.model.BNode;
+import org.openrdf.model.Resource;
+import org.openrdf.model.URI;
+
+import java.util.Map;
+
+/**
+ * Add file description here!
+ * <p/>
+ * User: sschaffe
+ */
+public class ResourceField implements Field {
+
+    private Resource resource;
+
+    public ResourceField(Resource resource) {
+        this.resource = resource;
+    }
+
+    public Resource getResource() {
+        return resource;
+    }
+
+    public void setResource(Resource resource) {
+        this.resource = resource;
+    }
+
+    public String toString() {
+        return toString(null);
+    }
+
+    /**
+     * Create string representation taking into account the namespace definitions given as argument.
+     *
+     * @param namespaces
+     * @return
+     */
+    @Override
+    public String toString(Map<String, String> namespaces) {
+        if(getResource() instanceof URI) {
+            String uri = getResource().stringValue();
+            if(namespaces != null) {
+                for(Map.Entry<String,String> ns : namespaces.entrySet()) {
+                    if(uri.startsWith(ns.getValue())) {
+                        return ns.getKey() + ":" + uri.substring(ns.getValue().length());
+                    }
+                }
+            }
+            return "<" + uri + ">";
+        } else if(getResource() instanceof BNode) {
+            return "_:"+getResource().stringValue();
+        } else {
+            return null;
+        }
+    }
+
+
+    @Override
+    public boolean isResourceField() {
+        return true;
+    }
+
+    @Override
+    public boolean isLiteralField() {
+        return false;
+    }
+
+    @Override
+    public boolean isVariableField() {
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ResourceField that = (ResourceField) o;
+
+        if (resource != null ? !resource.equals(that.resource) : that.resource != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return resource != null ? resource.hashCode() : 0;
+    }
+
+}


Mime
View raw message