1   /*
2    * RDFpro - An extensible tool for building stream-oriented RDF processing libraries.
3    * 
4    * Written in 2015 by Francesco Corcoglioniti with support by Alessio Palmero Aprosio and Marco
5    * Rospocher. Contact info on http://rdfpro.fbk.eu/
6    * 
7    * To the extent possible under law, the authors have dedicated all copyright and related and
8    * neighboring rights to this software to the public domain worldwide. This software is
9    * distributed without any warranty.
10   * 
11   * You should have received a copy of the CC0 Public Domain Dedication along with this software.
12   * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13   */
14  package eu.fbk.rdfpro.util;
15  
16  import java.util.HashSet;
17  import java.util.Iterator;
18  import java.util.Objects;
19  import java.util.Set;
20  
21  import javax.annotation.Nullable;
22  
23  import org.openrdf.model.Namespace;
24  import org.openrdf.model.Resource;
25  import org.openrdf.model.Statement;
26  import org.openrdf.model.URI;
27  import org.openrdf.model.Value;
28  import org.openrdf.model.impl.NamespaceImpl;
29  import org.openrdf.model.util.ModelException;
30  import org.openrdf.query.Binding;
31  import org.openrdf.query.BindingSet;
32  import org.openrdf.query.Dataset;
33  import org.openrdf.query.MalformedQueryException;
34  import org.openrdf.query.QueryEvaluationException;
35  import org.openrdf.query.QueryLanguage;
36  import org.openrdf.query.TupleQuery;
37  import org.openrdf.query.algebra.TupleExpr;
38  import org.openrdf.repository.RepositoryConnection;
39  import org.openrdf.repository.RepositoryException;
40  import org.openrdf.repository.RepositoryResult;
41  
42  final class QuadModelRepositoryAdapter extends QuadModel implements AutoCloseable {
43  
44      private static final long serialVersionUID = 1L;
45  
46      private final RepositoryConnection connection;
47  
48      private final boolean trackChanges;
49  
50      QuadModelRepositoryAdapter(final RepositoryConnection connection, final boolean trackChanges) {
51          this.connection = Objects.requireNonNull(connection);
52          this.trackChanges = trackChanges;
53      }
54  
55      @Override
56      public void close() {
57          IO.closeQuietly(this.connection);
58      }
59  
60      @Override
61      protected Set<Namespace> doGetNamespaces() {
62          try {
63              final Set<Namespace> namespaces = new HashSet<>();
64              RepositoryResult<? extends Namespace> iteration;
65              iteration = this.connection.getNamespaces();
66              try {
67                  while (iteration.hasNext()) {
68                      namespaces.add(iteration.next());
69                  }
70              } finally {
71                  iteration.close();
72              }
73              return namespaces;
74          } catch (final RepositoryException ex) {
75              throw new ModelException(ex);
76          }
77      }
78  
79      @Override
80      @Nullable
81      protected Namespace doGetNamespace(final String prefix) {
82          try {
83              final String name = this.connection.getNamespace(prefix);
84              return name == null ? null : new NamespaceImpl(prefix, name);
85          } catch (final RepositoryException ex) {
86              throw new ModelException(ex);
87          }
88      }
89  
90      @Override
91      protected Namespace doSetNamespace(final String prefix, @Nullable final String name) {
92          try {
93              final String oldName = this.connection.getNamespace(prefix);
94              final Namespace oldNamespace = oldName == null ? null : new NamespaceImpl(prefix,
95                      oldName);
96              if (name == null) {
97                  this.connection.removeNamespace(prefix);
98              } else {
99                  this.connection.setNamespace(prefix, name);
100             }
101             return oldNamespace;
102         } catch (final RepositoryException ex) {
103             throw new ModelException(ex);
104         }
105     }
106 
107     @Override
108     protected int doSize(@Nullable final Resource subj, @Nullable final URI pred,
109             @Nullable final Value obj, final Resource[] ctxs) {
110         try {
111             if (subj == null && pred == null && obj == null) {
112                 return (int) this.connection.size(ctxs);
113             } else {
114                 int size = 0;
115                 RepositoryResult<? extends Statement> iteration;
116                 iteration = this.connection.getStatements(subj, pred, obj, false, ctxs);
117                 try {
118                     while (iteration.hasNext()) {
119                         iteration.next();
120                         ++size;
121                     }
122                 } finally {
123                     iteration.close();
124                 }
125                 return size;
126             }
127         } catch (final RepositoryException ex) {
128             throw new ModelException(ex);
129         }
130     }
131 
132     @Override
133     protected int doSizeEstimate(@Nullable final Resource subj, @Nullable final URI pred,
134             @Nullable final Value obj, @Nullable final Resource ctx) {
135         return Integer.MAX_VALUE; // no way to efficiently estimate cardinality
136     }
137 
138     @Override
139     protected Iterator<Statement> doIterator(@Nullable final Resource subj,
140             @Nullable final URI pred, @Nullable final Value obj, final Resource[] ctxs) {
141         try {
142             return Iterators.forIteration(this.connection.getStatements(subj, pred, obj, false,
143                     ctxs));
144         } catch (final RepositoryException ex) {
145             throw new ModelException(ex);
146         }
147     }
148 
149     @Override
150     protected boolean doAdd(@Nullable final Resource subj, @Nullable final URI pred,
151             @Nullable final Value obj, final Resource[] ctxs) {
152         try {
153             if (!this.trackChanges) {
154                 this.connection.add(subj, pred, obj, ctxs);
155                 return true;
156             } else if (ctxs.length == 0) {
157                 if (this.connection.hasStatement(subj, pred, obj, false, new Resource[] { null })) {
158                     return false;
159                 }
160                 this.connection.add(subj, pred, obj, ctxs);
161                 return true;
162             } else if (ctxs.length == 1) {
163                 if (this.connection.hasStatement(subj, pred, obj, false, ctxs)) {
164                     return false;
165                 }
166                 this.connection.add(subj, pred, obj, ctxs);
167                 return true;
168             } else {
169                 boolean modified = false;
170                 for (final Resource ctx : ctxs) {
171                     final Resource[] singletonCtxs = new Resource[] { ctx };
172                     if (!this.connection.hasStatement(subj, pred, obj, false, singletonCtxs)) {
173                         this.connection.add(subj, pred, obj, singletonCtxs);
174                         modified = true;
175                     }
176                 }
177                 return modified;
178             }
179         } catch (final RepositoryException ex) {
180             throw new ModelException(ex);
181         }
182     }
183 
184     @Override
185     protected boolean doRemove(@Nullable final Resource subj, @Nullable final URI pred,
186             @Nullable final Value obj, final Resource[] ctxs) {
187         try {
188             if (!this.trackChanges) {
189                 this.connection.remove(subj, pred, obj, ctxs);
190                 return true;
191             } else {
192                 if (!this.connection.hasStatement(subj, pred, obj, false, ctxs)) {
193                     return false;
194                 }
195                 this.connection.remove(subj, pred, obj, ctxs);
196                 return true;
197             }
198         } catch (final RepositoryException ex) {
199             throw new ModelException(ex);
200         }
201     }
202 
203     @Override
204     protected Iterator<BindingSet> doEvaluate(final TupleExpr expr, final Dataset dataset,
205             final BindingSet bindings) {
206 
207         final String queryString = Algebra.renderQuery(expr, null, null, true);
208         try {
209             final TupleQuery query = this.connection.prepareTupleQuery(QueryLanguage.SPARQL,
210                     queryString);
211             query.setDataset(dataset);
212             for (final Binding binding : bindings) {
213                 query.setBinding(binding.getName(), binding.getValue());
214             }
215             return Iterators.forIteration(query.evaluate());
216         } catch (final QueryEvaluationException | MalformedQueryException | RepositoryException ex) {
217             throw new ModelException(ex);
218         }
219     }
220 
221 }