View Javadoc
1   /*
2    * Copyright 2013-2023 Medical Information Systems Research Group (https://medical.zcu.cz),
3    * Department of Computer Science and Engineering, University of West Bohemia.
4    * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5    *
6    * This file is part of Sparkle project.
7    *
8    * Sparkle is free software: you can redistribute it and/or modify
9    * it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License.
11   *
12   * Sparkle is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with Sparkle. If not, see <http://www.gnu.org/licenses/>.
19   */
20  package cz.zcu.mre.sparkle.data.resourceStorage;
21  
22  import cz.zcu.mre.sparkle.Messages;
23  import cz.zcu.mre.sparkle.SparklePreferences;
24  import cz.zcu.mre.sparkle.data.DataAgent;
25  import cz.zcu.mre.sparkle.data.dataAgentFactory.DataAgentFactory;
26  import cz.zcu.mre.sparkle.gui.dialogs.other.ErrorDialog;
27  import cz.zcu.mre.sparkle.gui.dialogs.other.ProgressDialog;
28  import cz.zcu.mre.sparkle.tools.CancellableConsumer;
29  import javafx.beans.property.SimpleBooleanProperty;
30  import javafx.beans.value.ChangeListener;
31  import javafx.beans.value.ObservableBooleanValue;
32  import javafx.concurrent.Task;
33  import javafx.stage.Stage;
34  import org.apache.jena.query.QuerySolution;
35  import org.apache.jena.rdf.model.Property;
36  import org.apache.jena.rdf.model.RDFNode;
37  import org.apache.jena.rdf.model.Resource;
38  import org.apache.jena.vocabulary.OWL;
39  import org.apache.jena.vocabulary.RDF;
40  import java.io.File;
41  import java.io.Serializable;
42  import java.lang.reflect.Field;
43  import java.util.List;
44  import java.util.Set;
45  import java.util.concurrent.ExecutionException;
46  import java.util.logging.Level;
47  import java.util.logging.Logger;
48  
49  /**
50   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
51   * @author Klara Hlavacova
52   */
53  class ResourceStorageUpdater
54          implements Serializable {
55  
56      private static final long serialVersionUID = 324354987984654654L;
57  
58      private static final Logger LOG = Logger.getLogger(ResourceStorageUpdater.class.getName());
59  
60      private final ResourcesStorage resourcesStorage;
61  
62      public ResourceStorageUpdater(ResourcesStorage resourcesStorage) {
63          this.resourcesStorage = resourcesStorage;
64      }
65  
66      /**
67       * Přidá pojmy z uvedeného jmenného prostoru z úložiště.
68       *
69       * @param namespace Jmenný prostor.
70       * @param includeNamedIndividuals Pokud je <code>false</code>, budou
71       * filtrovány instance owl:NamedIndividual.
72       * @param cancelled Sledovatelná {@link Boolean} hodnota. Pokud je nastavena
73       * na <code>true</code>, bude požádáno o přerušení operace.
74       *
75       * @return Počet přidaných pojmů.
76       */
77      public final int addFromStorage(
78              final String namespace, final boolean includeNamedIndividuals, final ObservableBooleanValue cancelled) {
79  
80          return addFromStorage(namespace, includeNamedIndividuals, cancelled, resourcesStorage.getDataAgent());
81      }
82  
83      /**
84       * Přidá pojmy z uvedeného jmenného prostoru z úložiště.
85       *
86       * @param namespace Jmenný prostor.
87       * @param includeNamedIndividuals Pokud je <code>false</code>, budou
88       * filtrovány instance owl:NamedIndividual.
89       * @param cancelled Sledovatelná {@link Boolean} hodnota. Pokud je nastavena
90       * na <code>true</code>, bude požádáno o přerušení operace.
91       * @param dataAgent Zdroj dat.
92       *
93       * @return Počet přidaných pojmů.
94       */
95      public int addFromStorage(final String namespace, final boolean includeNamedIndividuals,
96              final ObservableBooleanValue cancelled, final DataAgent dataAgent) {
97          final int[] result = {0};
98  
99          List<String> langs = ResourceQueryBuilder.getCommaSeparatedPreferences(SparklePreferences.TITLES_LANG);
100         List<String> titles = ResourceQueryBuilder.getCommaSeparatedPreferences(SparklePreferences.TITLES);
101         List<String> langVars = ResourceQueryBuilder.getVarsNames(langs, titles);
102 
103         final CancellableConsumer<QuerySolution> consumer = new CancellableConsumer<QuerySolution>() {
104             @Override
105             public void accept(final QuerySolution qs) {
106                 if (addFromQuerySolution(qs, "r", langVars)) { //$NON-NLS-2$
107                     result[0]++;
108                 }
109             }
110         };
111 
112         final ChangeListener<Boolean> cancellationListener = (observable, oldValue, newValue) -> {
113             if (newValue) {
114                 consumer.cancel();
115             }
116         };
117 
118         cancelled.addListener(cancellationListener);
119 
120         String query = ResourceQueryBuilder.getQuery(namespace, includeNamedIndividuals, langs, titles, langVars,
121                 resourcesStorage.getPrefixesStorage().getPrefixToIri());
122         dataAgent.select(query, consumer);
123 
124         cancelled.removeListener(cancellationListener);
125 
126         return result[0];
127     }
128 
129     /**
130      * Ulozi zdroj ziskany dotazem
131      * <p>
132      * Pro použití pouze z
133      * {@link #addFromStorage(String, boolean, ObservableBooleanValue, DataAgent)}.
134      *
135      * @param qs {@code QuerySolution}
136      * @param varName jmeno promenne se zdrojem
137      * @param titles seznam promennych s titulky
138      */
139     @SuppressWarnings("SameParameterValue")
140     private boolean addFromQuerySolution(final QuerySolution qs, final String varName,
141             List<String> titles) {
142 
143         if (!qs.contains(varName)) {
144             return false;
145         }
146         String title = "";
147         for (String lang : titles) {
148             if (!qs.contains(lang)) {
149                 continue;
150             }
151             title = qs.get(lang).asLiteral().getString();
152             if (!title.isEmpty()) {
153                 break;
154             }
155         }
156 
157         final RDFNode resourceNode = qs.get(varName);
158         if (!resourceNode.isResource()) {
159             return false;
160         }
161 
162         final Resource resource = resourceNode.asResource();
163         return addPropertyOrResource(resource, title);
164     }
165 
166     /**
167      * Pokusí se o získání objektu {@link Resource} z předaného atributu třídy,
168      * který poté přidá na seznam.
169      *
170      * @param field Atribut.
171      *
172      * @return Výsledek operace.
173      *
174      * @throws IllegalArgumentException
175      * @throws IllegalAccessException
176      */
177     boolean addFromField(final Field field)
178             throws IllegalArgumentException, IllegalAccessException {
179         if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())
180                 || !Resource.class.isAssignableFrom(field.getType())) {
181             return false;
182         }
183 
184         final Object o = field.get(null);
185         if (o == null) {
186             return false;
187         }
188 
189         final Resource r = Resource.class.cast(field.get(null));
190         if (r.getLocalName().isEmpty()) {
191             return false;
192         }
193 
194         return addPropertyOrResource(r, "");
195     }
196 
197     /**
198      * Přidá zdroj. Nejprve ale zkontroluje, jestli jde o vlastnost. Tato
199      * schopnost je závislá na připojeném úložišti, resp. to, jestli je zdroj
200      * přidán jako instance rdf:Property závisí na jeho obsahu.
201      *
202      * @param r Zdroj.
203      * @param title popisek zdroje (rdfs:label, ...)
204      *
205      * @return Výsledek operace.
206      */
207     private boolean addPropertyOrResource(Resource r, String title) {
208         if (r instanceof Property) {
209             return addPropertyOrResource((Property) r, title);
210         }
211         if (r.getModel() == null) {
212             r = resourcesStorage.getDataAgent().inModel(r);
213         }
214         //TODO:????
215         boolean hasOWLProperty = r.hasProperty(RDF.type, OWL.ObjectProperty)
216                 || r.hasProperty(RDF.type, OWL.FunctionalProperty)
217                 || r.hasProperty(RDF.type, OWL.DatatypeProperty)
218                 || r.hasProperty(RDF.type, OWL.OntologyProperty)
219                 || r.hasProperty(RDF.type, OWL.AnnotationProperty)
220                 || r.hasProperty(RDF.type, OWL.SymmetricProperty)
221                 || r.hasProperty(RDF.type, OWL.TransitiveProperty);
222         boolean hasRdfProperty = r.hasProperty(RDF.type, RDF.Property);
223 
224         return resourcesStorage.addTerm(hasRdfProperty || hasOWLProperty
225                 ? new PropertyStorageEntry(r.as(Property.class), title)
226                 : new ResourceStorageEntry(r, title));
227     }
228 
229     /**
230      * Přidá vlastnost.
231      *
232      * @param p Vlastnost.
233      *
234      * @return Výsledek operace.
235      */
236     private boolean addPropertyOrResource(Property p, String title) {
237         if (p.getModel() == null) {
238             p = resourcesStorage.getDataAgent().inModel(p);
239         }
240         return resourcesStorage.addTerm(new PropertyStorageEntry(p, title));
241     }
242 
243     /**
244      * Aktualizace uloziste (po nahrani dat do uloziste)
245      *
246      * @param stage Instance {@link Stage}
247      * @param files seznam souboru s daty
248      * @param namespaces seznam jmennych prostoru
249      */
250     public void updateResourceStorage(Stage stage, List<File> files, Set<String> namespaces) {
251 
252         final Task<Boolean> task = new Task<Boolean>() {
253             @Override
254             protected Boolean call() throws Exception {
255 
256                 updateMessage(Messages.getString("TERMS_LOADING_IN_PROGRESS"));
257 
258                 final SimpleBooleanProperty cancelled = new SimpleBooleanProperty(false);
259 
260                 int index = 1;
261                 updateProgress(0, files.size());
262 
263                 for (File file : files) {
264                     DataAgent tempDA = DataAgentFactory.createTemp();
265                     tempDA.addToModel(file);
266 
267                     for (String namespace : namespaces) {
268                         updateTitle(Messages.getString("UPDATING") + ": " + index + "/"
269                                 + (files.size() * namespaces.size()));//$NON-NLS-1$
270                         index = updateStorageFromFile(cancelled, index, file, tempDA, namespace);
271                     }
272                 }
273                 return true;
274             }
275 
276             /**
277              * Aktualizuje jedno uloziste ze souboru
278              *
279              * @param cancelled true - ukonceno
280              * @param index index
281              * @param file nahravany soubor
282              * @param tempDA data agent
283              * @param namespace jmenny prostor
284              *
285              * @return novy index
286              */
287             private int updateStorageFromFile(SimpleBooleanProperty cancelled, int index, File file, DataAgent tempDA,
288                     String namespace) {
289                 try {
290                     updateMessage(file.getName() + "\n" + namespace + "\n"
291                             + Messages.getString("TERMS_LOADING_IN_PROGRESS"));
292 
293                     addFromStorage(namespace, true, cancelled, tempDA);
294                     updateProgress(++index, files.size());
295 
296                 } catch (Error e) {
297                     ErrorDialog.open(stage, Messages.getString("ERROR"),
298                             Messages.getString("OPERATION_FAILED"), e);
299                     LOG.log(Level.SEVERE, "Exception: ", e); //$NON-NLS-2$
300                 }
301                 return index;
302             }
303 
304         };
305 
306         performUpdateStorageFromFIle(stage, task, true);
307     }
308 
309     /**
310      * Aktualizace uloziste z url
311      *
312      * @param stage Instance {@link Stage}
313      * @param namespace jmenny prostor
314      * @param size pocet namespace, ktere se aktualizuje
315      * @param index index nacitane uri
316      */
317     public int updateResourceStorage(Stage stage, String namespace, int size, int index) {
318         return updateResourceStorage(stage, namespace, size, index, true);
319     }
320 
321     /**
322      * Aktualizace uloziste z url
323      *
324      * @param stage Instance {@link Stage}
325      * @param namespace jmenny prostor
326      * @param size pocet namespace, ktere se aktualizuje
327      * @param index index nacitane uri
328      * @param includeIndividuals {@code true} - zahrnout individualy
329      */
330     public int updateResourceStorage(Stage stage, String namespace, int size, int index, boolean includeIndividuals) {
331 
332         int updated = index;
333 
334         final Task<Boolean> task = new Task<Boolean>() {
335             @Override
336             protected Boolean call() {
337 
338                 LOG.log(Level.INFO, "Updating {0}: {1}/{2}", new Object[]{namespace, updated, size});//$NON-NLS-1$
339 
340                 updateTitle(Messages.getString("UPDATING") + ": " + updated + "/" + size);//$NON-NLS-1$
341                 String prefix = resourcesStorage.getPrefixesStorage().getNsURIPrefix(namespace);
342                 updateMessage(prefix + ": " + namespace + "\n" + Messages.getString("TERMS_LOADING_IN_PROGRESS"));
343 
344                 final SimpleBooleanProperty cancelled = new SimpleBooleanProperty(false);
345 
346                 updateProgress(0, 1);
347 
348                 DataAgent tempDA = DataAgentFactory.createTemp();
349 
350                 try {
351                     tempDA.getFullModel().read(namespace);
352 
353                     addFromStorage(namespace, includeIndividuals, cancelled, tempDA);
354                     updateProgress(1, 1);
355 
356                 } catch (Exception | Error e) {
357                     LOG.log(Level.INFO, "Exception: {0}\n{1}", new Object[]{namespace, e.getCause().getLocalizedMessage()}); //$NON-NLS-2$
358                     return false;
359                 }
360 
361                 return true;
362             }
363 
364         };
365 
366         if (!performUpdateStorageFromFIle(stage, task, false)) {
367             return index;
368         }
369 
370         return ++index;
371     }
372 
373     /**
374      * Provede aktualizaci uloziste
375      *
376      * @param stage scena
377      * @param task spoustena uloha
378      */
379     private boolean performUpdateStorageFromFIle(Stage stage, Task<Boolean> task, boolean showDialog) {
380         try {
381             return ProgressDialog.performTask(stage, task);
382 
383         } catch (final OutOfMemoryError e) {
384             ErrorDialog.open(stage, Messages.getString("ERROR"),
385                     Messages.getString("OUT_OF_MEMORY_FILE_LARGE"), e);
386 
387             return false;
388 
389         } catch (InterruptedException | ExecutionException | Error e) {
390             LOG.log(Level.INFO, "Exception: {0}", e.getCause().getLocalizedMessage()); //$NON-NLS-2$
391             if (showDialog) {
392                 ErrorDialog.open(stage, Messages.getString("ERROR"), Messages.getString("IMPORT_FAILED"), e);
393             }
394             return false;
395         }
396     }
397 
398 }