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.gui.dialogs.storage;
21  
22  import cz.zcu.mre.sparkle.Messages;
23  import cz.zcu.mre.sparkle.data.PrefixesStorage;
24  import cz.zcu.mre.sparkle.gui.dialogs.other.ErrorDialog;
25  import cz.zcu.mre.sparkle.gui.tools.AbstractDialogController;
26  import cz.zcu.mre.sparkle.gui.tools.FormControllerFactory;
27  import cz.zcu.mre.sparkle.gui.tools.ShortCutHandler;
28  import cz.zcu.mre.sparkle.tools.Definitions;
29  import cz.zcu.mre.sparkle.tools.IO;
30  import javafx.beans.property.SimpleStringProperty;
31  import javafx.collections.FXCollections;
32  import javafx.collections.ObservableList;
33  import javafx.event.ActionEvent;
34  import javafx.fxml.FXML;
35  import javafx.scene.control.*;
36  import javafx.scene.control.TableColumn.CellEditEvent;
37  import javafx.scene.control.cell.PropertyValueFactory;
38  import javafx.scene.control.cell.TextFieldTableCell;
39  import javafx.scene.input.KeyCode;
40  import javafx.stage.Window;
41  import java.io.File;
42  import java.io.IOException;
43  import java.util.Collection;
44  import java.util.HashSet;
45  import java.util.List;
46  import java.util.Set;
47  import java.util.logging.Level;
48  import java.util.logging.Logger;
49  
50  /**
51   * Dialog pro správu mapování prefixů na IRI jmenných prostorů.
52   *
53   * @author Jan Smucr
54   * @author Klara Hlavacova
55   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
56   */
57  public final class PrefixesDialog
58          extends AbstractDialogController {
59  
60      private static final Logger LOG = Logger.getLogger(PrefixesDialog.class.getName());
61      public static final int CSV_COLUMN_COUNT = 5;
62  
63      enum CSV_FILE_ROW {
64          NAME(0), DATE(1), PREFIX(2), IRI(3), ALTERNATIVE_IRI(4);
65  
66          private int index;
67  
68          CSV_FILE_ROW(int index) {
69              this.index = index;
70          }
71  
72          public int getIndex() {
73              return index;
74          }
75      }
76  
77      @FXML
78      private TextField prefixTextField, iriTextField, alternativeIriTextField;
79      @FXML
80      private TableView<TableRow> prefixesTable;
81      @FXML
82      private TableColumn<TableRow, String> prefixColumn, iriColumn, alternativeIriColumn;
83      @FXML
84      private Button removeSelectedButton, addButton;
85  
86      private PrefixesStorage prefixesStorage = null;
87  
88      /**
89       * Uchovává seznam prefixů, které jsou právě používány. Nastavením lze
90       * zabránit tomu, aby uživatel smazal prefix, který v dotazu právě používá.
91       * Jinak by takový dotaz nešel přeparsovat.
92       */
93      private final Set<String> prefixesInUse = new HashSet<>();
94  
95      @Override
96      protected final void onDialogInitialized() {
97          setTitle(Messages.getString("MANAGE_PREFIXES"));
98  
99          prefixColumn.setCellFactory(TextFieldTableCell.forTableColumn());
100         prefixColumn.setCellValueFactory(new PropertyValueFactory<>("prefix")); //$NON-NLS-1$
101         iriColumn.setCellFactory(TextFieldTableCell.forTableColumn());
102         iriColumn.setCellValueFactory(new PropertyValueFactory<>("iri")); //$NON-NLS-1$
103         alternativeIriColumn.setCellFactory(TextFieldTableCell.forTableColumn());
104         alternativeIriColumn.setCellValueFactory(new PropertyValueFactory<>("alternativeIri")); //$NON-NLS-1$
105 
106         prefixesTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
107         removeSelectedButton.disableProperty().bind(prefixesTable.getSelectionModel().selectedItemProperty().isNull());
108 
109         addButton.disableProperty()
110                 .bind(prefixTextField.textProperty().isEmpty().or(iriTextField.textProperty().isEmpty()));
111 
112         // Mazání položek v tabulce klávesou delete
113         getShortCutHandler();
114     }
115 
116     /**
117      * Posluchac pro mazání vybraných položek klávesou delete
118      */
119     private void getShortCutHandler() {
120         new ShortCutHandler(KeyCode.DELETE) {
121 
122             @Override
123             public final boolean handle(final Object source) {
124                 if (source != prefixesTable) {
125                     return false;
126                 }
127 
128                 if (prefixesTable.getSelectionModel().isEmpty()) {
129                     return false;
130                 }
131 
132                 removeSelectedButtonOnAction();
133                 return true;
134             }
135         }.attach(prefixesTable);
136     }
137 
138     /**
139      *
140      */
141     @FXML
142     private void addButtonOnAction() {
143         final String prefix = prefixTextField.getText().trim();
144         final String iri = iriTextField.getText().trim();
145         final String alternativeIri = alternativeIriTextField.getText().trim();
146 
147         if (!formCheck(prefix, iri, alternativeIri)) {
148             return;
149         }
150 
151         addPrefix(prefix, iri, alternativeIri);
152 
153         prefixTextField.clear();
154         iriTextField.clear();
155         alternativeIriTextField.clear();
156     }
157 
158     /**
159      * Vlozi prefix do uloziste
160      *
161      * @param prefix prefix
162      * @param iri iri
163      * @param alternativeIri alternativni iri
164      */
165     private void addPrefix(String prefix, String iri, String alternativeIri) {
166 
167         final Collection<TableRow> items = prefixesTable.getItems();
168         final int index = items.size();
169 
170         items.add(new TableRow(prefix, iri, alternativeIri));
171         prefixesStorage.addPrefix(prefix, iri, alternativeIri);
172 
173         prefixesTable.scrollTo(index);
174         prefixesTable.getSelectionModel().clearAndSelect(index);
175     }
176 
177     /**
178      * Kontrola poli pro pridani noveho prefixu
179      *
180      * @param prefix prefix
181      * @param iri IRI
182      * @param alternativeIri alternativni IRI
183      *
184      * @return true - ok
185      */
186     private boolean formCheck(String prefix, String iri, String alternativeIri) {
187         if (prefix.isEmpty()) {
188             showErrorDialog("WARNING", Messages.getString("PREFIX_CANNOT_BE_EMPTY"));
189             prefixTextField.requestFocus();
190             return false;
191         }
192 
193         if (prefixesStorage.getPrefixToIri().containsKey(prefix)) {
194             showErrorDialog("WARNING", Messages.getString("PREFIX_ALREADY_EXISTS"));
195             prefixTextField.requestFocus();
196             return false;
197         }
198 
199         if (prefixesStorage.getNsURIPrefix(iri) != null) {
200             showErrorDialog("WARNING", String.format(Messages.getString("IRI_ALREADY_EXISTS"), prefix));
201             prefixTextField.requestFocus();
202             return false;
203         }
204 
205         if (prefixesStorage.getNsURIPrefix(alternativeIri) != null) {
206             showErrorDialog("WARNING", String.format(Messages.getString("ALT_IRI_ALREADY_EXISTS"), prefix));
207             prefixTextField.requestFocus();
208             return false;
209         }
210 
211         if (iri.isEmpty()) {
212             showErrorDialog("WARNING", Messages.getString("IRI_CANNOT_BE_EMPTY"));
213             prefixTextField.requestFocus();
214             return false;
215         }
216 
217         return true;
218     }
219 
220     /**
221      *
222      */
223     @FXML
224     private void removeSelectedButtonOnAction() {
225         // JavaFX bug RT-24367: Need to make a copy of the list
226         final Collection<TableRow> selectedItems
227                 = FXCollections.observableArrayList(prefixesTable.getSelectionModel().getSelectedItems());
228         if ((selectedItems.isEmpty())) {
229             return;
230         }
231 
232         final Collection<TableRow> items = prefixesTable.getItems();
233         boolean anythingRemoved = false;
234 
235         for (final TableRow item : selectedItems) {
236             final String prefix = item.getPrefix();
237 
238             if (prefixesInUse.contains(prefix)) {// Odmítnutí odstranit prefix, který se právě používá
239                 showErrorDialog("WARNING", String.format(Messages.getString("PREFIX_IN_USE_REMOVAL"), prefix));
240                 continue;
241             }
242             prefixesStorage.removeNsPrefix(prefix);
243             items.remove(item);
244             anythingRemoved = true;
245         }
246 
247         if (anythingRemoved) {
248             prefixesTable.getSelectionModel().clearSelection();
249         }
250     }
251 
252     /**
253      *
254      */
255     @FXML
256     private void restorePrefixesButtonOnAction() {
257         prefixesStorage.add(PrefixesStorage.getBuiltIn());
258         setPrefixesStorage(prefixesStorage);
259     }
260 
261     /**
262      * Nahrani prefixu ze souboru.
263      *
264      * @param actionEvent An action event.
265      */
266     @FXML
267     public void loadFromFileButtonOnAction(ActionEvent actionEvent) {
268         List<File> files = IO.openCsvFile(this.getStage(), "", false);
269         if (files == null || files.isEmpty()) {
270             return;
271         }
272         List<String> rows = IO.readStringsFromFile(files.get(0));
273         if (rows == null) {
274             return;
275         }
276         for (String row : rows) {
277             String[] values = row.split(Definitions.SEMICOLON);
278             if (values.length < CSV_COLUMN_COUNT) {
279                 ErrorDialog.open(this.getStage(), Messages.getString("WARNING"),
280                         String.format(Messages.getString("LOAD_PREFIX_FROM_FILE_ERROR"), row));
281                 continue;
282             }
283 
284             String prefix = values[CSV_FILE_ROW.PREFIX.getIndex()];
285             String iri = values[CSV_FILE_ROW.IRI.getIndex()];
286             String altIri = values[CSV_FILE_ROW.ALTERNATIVE_IRI.getIndex()];
287             altIri = altIri.equals("-") ? "" : altIri;
288 
289             if (prefixesStorage.getPrefixToIri().containsKey(prefix)) {
290                 // showErrorDialog("WARNING", prefix + ": " + Messages.getString("PREFIX_ALREADY_EXISTS"));
291                 continue;
292             }
293             //TODO: aktualizace
294 
295             if (prefixesStorage.getNsURIPrefix(iri) != null) {
296                 //  showErrorDialog("WARNING",iri + ": " + String.format(Messages.getString("IRI_ALREADY_EXISTS"), prefix));
297                 continue;
298             }
299 
300             if (prefixesStorage.getNsURIPrefix(altIri) != null) {
301                 //  showErrorDialog("WARNING",altIri + ": " + String.format(Messages.getString("ALT_IRI_ALREADY_EXISTS"), prefix));
302                 continue;
303             }
304 
305             addPrefix(prefix, iri, altIri);
306         }
307 
308         //TODO: added x prefixes
309     }
310 
311     /**
312      *
313      */
314     @FXML
315     private void closeButtonOnAction() {
316         close();
317     }
318 
319     /**
320      * @param prefixesStorage
321      */
322     private void setPrefixesStorage(final PrefixesStorage prefixesStorage) {
323         final ObservableList<TableRow> data = FXCollections.observableArrayList();
324 
325         prefixesStorage.getPrefixToIri().forEach((key, value) -> {
326             data.add(new TableRow(key, value, prefixesStorage.getIriToAlternativeIri().get(value)));
327         });
328         prefixesTable.setItems(data);
329 
330         this.prefixesStorage = prefixesStorage;
331     }
332 
333     /**
334      * @param owner The window owner.
335      * @param prefixesStorage Prefixes storage.
336      */
337     public static final void open(final Window owner, final PrefixesStorage prefixesStorage) {
338         open(owner, prefixesStorage, null);
339     }
340 
341     /**
342      * @param owner The windows owner.
343      * @param prefixesStorage Prefixes storage.
344      * @param prefixesInUse Set of used prefixes.
345      */
346     public static final void open(final Window owner, final PrefixesStorage prefixesStorage,
347             final Set<String> prefixesInUse) {
348         try {
349             final PrefixesDialog dlg = FormControllerFactory.load(owner, PrefixesDialog.class);
350             dlg.setPrefixesStorage(prefixesStorage);
351             dlg.setPrefixesInUse(prefixesInUse);
352             dlg.showAndWait();
353         } catch (final IOException e) {
354             LOG.log(Level.SEVERE, "Exception: ", e);//$NON-NLS-1$
355         }
356     }
357 
358     /**
359      * Nastaví prefixy jejichž smazání má být zabráněno.
360      *
361      * @param prefixesInUse Prefixy jejichž smazání má být zabráněno.
362      */
363     private void setPrefixesInUse(final Set<String> prefixesInUse) {
364         this.prefixesInUse.clear();
365         if (prefixesInUse != null) {
366             this.prefixesInUse.addAll(prefixesInUse);
367         }
368     }
369 
370     /**
371      * @param event
372      */
373     @FXML
374     private void prefixOnEditCommit(final CellEditEvent<TableRow, String> event) {
375         final String newValue = event.getNewValue().trim();
376         final String oldValue = event.getOldValue();
377         boolean error = false;
378 
379         if (prefixesInUse.contains(oldValue)) {
380             // Právě používaný prefix nelze přejmenovat
381             error = true;
382             showErrorDialog("ERROR", String.format(Messages.getString("PREFIX_IN_USE_RENAMING"),
383                     oldValue));
384         }
385 
386         if (newValue.isEmpty()) {
387             error = true;
388             showErrorDialog("ERROR", Messages.getString("PREFIX_CANNOT_BE_EMPTY"));
389         } else if (prefixesStorage.getPrefixToIri().containsKey(newValue)) {
390             error = true;
391             showErrorDialog("ERROR", Messages.getString("PREFIX_ALREADY_EXISTS"));
392         }
393 
394         final TableRow item = event.getRowValue();
395 
396         if (error) {
397             item.setPrefix(""); // Jinak nedojde k updatu. //$NON-NLS-1$
398             item.setPrefix(oldValue);
399             return;
400         }
401 
402         item.setPrefix(newValue);
403         prefixesStorage.renamePrefix(oldValue, newValue);
404 
405         prefixesTable.requestFocus();
406     }
407 
408     /**
409      * @param event
410      */
411     @FXML
412     private void iriOnEditCommit(final CellEditEvent<TableRow, String> event) {
413         editIri(event, true);
414     }
415 
416     @FXML
417     private void alternativeIriOnEditCommit(final CellEditEvent<TableRow, String> event) {
418         editIri(event, false);
419     }
420 
421     /**
422      * Editace IRI nebo alternativni IRI
423      *
424      * @param event data
425      * @param editIri - true - zmena IRI, false - zmena alternativni IRI
426      */
427     private void editIri(CellEditEvent<TableRow, String> event, boolean editIri) {
428         final String newValue = event.getNewValue().trim();
429         final String oldValue = event.getOldValue();
430         boolean error = false;
431 
432         if (editIri && newValue.isEmpty()) {
433             error = true;
434             showErrorDialog("ERROR", Messages.getString("IRI_CANNOT_BE_EMPTY"));
435         }
436 
437         final TableRow item = event.getRowValue();
438 
439         if (error) {
440             item.setIri(""); // Jinak nedojde k updatu. //$NON-NLS-1$
441             item.setIri(oldValue);
442             return;
443         }
444 
445         if (editIri) {
446             item.setIri(newValue);
447             prefixesStorage.setNsPrefix(item.getPrefix(), newValue);
448         } else {
449             item.setAlternativeIri(newValue);
450             prefixesStorage.setAlternativeIri(item.getIri(), newValue);
451         }
452 
453         prefixesTable.requestFocus();
454     }
455 
456     /**
457      * Zobrazi dialog s chybou
458      *
459      * @param titleKey klic retezce - nadpis
460      * @param message zprava
461      */
462     private void showErrorDialog(String titleKey, String message) {
463         ErrorDialog.open(getStage(), Messages.getString(titleKey), message);
464     }
465 
466     /**
467      * Položka tabulky.
468      */
469     public static final class TableRow {
470 
471         private final SimpleStringProperty prefix;
472         private final SimpleStringProperty iri;
473         private final SimpleStringProperty alternativeIri;
474 
475         TableRow() {
476             prefix = new SimpleStringProperty();
477             iri = new SimpleStringProperty();
478             alternativeIri = new SimpleStringProperty();
479         }
480 
481         TableRow(final String prefix, final String iri, final String alternativeIri) {
482             this.prefix = new SimpleStringProperty(prefix);
483             this.iri = new SimpleStringProperty(iri);
484             this.alternativeIri = new SimpleStringProperty(alternativeIri);
485         }
486 
487         public final String getPrefix() {
488             return prefix.get();
489         }
490 
491         public final void setPrefix(final String value) {
492             prefix.set(value);
493         }
494 
495         public final String getIri() {
496             return iri.get();
497         }
498 
499         public final void setIri(final String value) {
500             iri.set(value);
501         }
502 
503         public String getAlternativeIri() {
504             return alternativeIri.get();
505         }
506 
507         public void setAlternativeIri(final String value) {
508             alternativeIri.set(value);
509         }
510 
511     }
512 }