1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package cz.zcu.mre.sparkle.gui.dialogs.query;
21
22 import cz.zcu.mre.sparkle.Messages;
23 import cz.zcu.mre.sparkle.data.DataAgent;
24 import cz.zcu.mre.sparkle.data.PrefixesStorage;
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.ReferenceKeeper;
28 import cz.zcu.mre.sparkle.tools.Utils;
29 import javafx.application.Platform;
30 import javafx.beans.Observable;
31 import javafx.beans.binding.BooleanExpression;
32 import javafx.beans.property.*;
33 import javafx.collections.FXCollections;
34 import javafx.collections.ObservableList;
35 import javafx.fxml.FXML;
36 import javafx.scene.control.Button;
37 import javafx.scene.control.ListView;
38 import javafx.scene.control.ProgressIndicator;
39 import javafx.scene.layout.Region;
40 import javafx.stage.Window;
41 import org.apache.jena.rdf.model.Property;
42 import org.apache.jena.rdf.model.Resource;
43 import java.io.IOException;
44 import java.util.LinkedList;
45 import java.util.List;
46 import java.util.Set;
47 import java.util.concurrent.ExecutionException;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50
51
52
53
54
55
56
57 public final class PathsDialog
58 extends AbstractDialogController {
59
60 private static final Logger LOG = Logger.getLogger(PathsDialog.class.getName());
61
62
63
64
65 private static final class PathsListItem {
66
67 private final ObjectProperty<LinkedList<Resource>> pathProperty = new SimpleObjectProperty<>(null);
68 private final Resource startNode, endNode;
69 private final StringProperty itemTextProperty = new SimpleStringProperty();
70 private final DataAgent dataAgent;
71 private final String startNodeText;
72 private final PrefixesStorage prefixesStorage;
73 private final BooleanProperty searchingProperty = new SimpleBooleanProperty(false);
74 private boolean cancelled = false;
75
76 public PathsListItem(final DataAgent dataAgent, final PrefixesStorage prefixesStorage, final Resource startNode,
77 final Resource endNode) {
78 this.startNode = startNode;
79 this.endNode = endNode;
80 this.dataAgent = dataAgent;
81 this.prefixesStorage = prefixesStorage;
82 this.startNodeText = prefixesStorage.shortForm(startNode.toString()) + " ... ";
83 itemTextProperty.set(startNodeText + Messages.getString("WAITING"));
84
85 }
86
87
88
89
90
91 public final void startSearch() {
92 cancelled = false;
93
94 new Thread(()
95 -> {
96 if (cancelled) {
97 return;
98 }
99
100 try {
101 Utils.runAndWait(()
102 -> {
103 searchingProperty.set(true);
104 itemTextProperty.set(startNodeText + Messages.getString("SEARCHING"));
105 });
106 } catch (final InterruptedException | ExecutionException e) {
107 LOG.log(Level.SEVERE, "Exception: ", e);
108 }
109
110 final LinkedList<Resource> result = dataAgent.findForwardOntologyPath(startNode, endNode);
111
112 try {
113 Platform.runLater(()
114 -> {
115 synchronized (pathProperty) {
116 pathProperty.set(result);
117 }
118 searchingProperty.set(false);
119 });
120 } catch (final Exception e) {
121 LOG.log(Level.SEVERE, "Exception: ", e);
122 }
123
124 if (cancelled) {
125 return;
126 }
127
128 if (result == null) {
129 Platform.runLater(
130 () -> itemTextProperty.set(startNodeText + Messages.getString("NOT_FOUND")));
131 return;
132 }
133
134
135 final StringBuilder sb = new StringBuilder();
136 result.stream().map((resource) -> {
137 sb.append(prefixesStorage.shortForm(resource.toString()));
138 return resource;
139 }).filter((resource) -> (!resource.equals(endNode))).forEach((_item) -> {
140 sb.append(" -> ");
141 });
142
143 Platform.runLater(() -> itemTextProperty.set(sb.toString()));
144 }).start();
145 }
146
147
148
149
150 public final void cancelSearch() {
151 cancelled = true;
152 }
153
154 public final boolean isCancelled() {
155 return cancelled;
156 }
157
158 public final LinkedList<Resource> getPath() {
159 synchronized (pathProperty) {
160 return pathProperty.get();
161 }
162 }
163
164 @SuppressWarnings("unused")
165 public final ReadOnlyObjectProperty<LinkedList<Resource>> getPathProperty() {
166 return new ReadOnlyObjectPropertyBase<LinkedList<Resource>>() {
167 @Override
168 public LinkedList<Resource> get() {
169 return pathProperty.get();
170 }
171
172 @Override
173 public Object getBean() {
174 return pathProperty.getBean();
175 }
176
177 @Override
178 public String getName() {
179 return pathProperty.getName();
180 }
181 };
182 }
183
184 @Override
185 public final String toString() {
186 return itemTextProperty.get();
187 }
188 }
189
190 private Set<Resource> startNodeTypes;
191 private Property endProperty;
192 private PrefixesStorage prefixesStorage;
193 private DataAgent dataAgent;
194 private LinkedList<Resource> result = null;
195 private final ReferenceKeeper keeper = new ReferenceKeeper();
196
197 @FXML
198 private ListView<PathsListItem> pathsList;
199 @FXML
200 private Button selectButton, cancelButton;
201 @FXML
202 private ProgressIndicator progressIndicator;
203 @FXML
204 private Region dummyRegion;
205
206 public static final LinkedList<Resource> open(final Window owner, final DataAgent dataAgent,
207 final PrefixesStorage prefixesStorage,
208 final Set<Resource> startNodeTypes, final Property endProperty) {
209 try {
210 final PathsDialog dlg = FormControllerFactory.load(owner, PathsDialog.class);
211 dlg.dataAgent = dataAgent;
212 dlg.prefixesStorage = prefixesStorage;
213 dlg.startNodeTypes = startNodeTypes;
214 dlg.endProperty = endProperty;
215 dlg.showAndWait();
216
217 return dlg.result;
218 } catch (final IOException e) {
219 LOG.log(Level.SEVERE, "Exception: ", e);
220 }
221 return null;
222 }
223
224 @Override
225 protected void onDialogInitialized() {
226 Utils.makeInvisibleUnmanaged(progressIndicator, dummyRegion);
227
228 dummyRegion.visibleProperty().bind(progressIndicator.visibleProperty());
229
230 getStage().setResizable(true);
231
232 pathsList.getSelectionModel().selectedItemProperty().addListener(keeper.toWeak((observable, oldValue, newValue)
233 -> {
234 updateSelectButton();
235 }));
236
237
238 pathsList.itemsProperty().addListener(keeper.toWeak((observable, oldValue, newValue)
239 -> {
240 BooleanExpression ex = BooleanProperty.booleanExpression(new SimpleBooleanProperty(false));
241
242 if (newValue != null) {
243 for (final PathsListItem pathsListItem : newValue) {
244 ex = ex.or(pathsListItem.searchingProperty);
245 }
246 }
247
248
249 progressIndicator.visibleProperty().bind(ex);
250 }));
251 }
252
253
254
255
256
257 private void updateSelectButton() {
258 final PathsListItem item = pathsList.getSelectionModel().getSelectedItem();
259 selectButton.setDisable((item == null) || (item.getPath() == null));
260 }
261
262 @Override
263 protected void onShowing() {
264
265 final ObservableList<PathsListItem> listItems = FXCollections.observableArrayList();
266 for (final Resource startNodeType : startNodeTypes) {
267 final PathsListItem listItem = new PathsListItem(dataAgent, prefixesStorage, startNodeType, endProperty);
268
269
270 listItem.searchingProperty.addListener((observable, oldValue, newValue)
271 -> {
272 if (!(oldValue && !newValue && !listItem.isCancelled())) {
273 return;
274 }
275 while (true) {
276 final int myIndex = listItems.indexOf(listItem);
277 if (pathsList.getSelectionModel().isSelected(myIndex)) {
278 updateSelectButton();
279 }
280
281 final int nextIndex = myIndex + 1;
282 if ((nextIndex < listItems.size()) && (nextIndex > 0)) {
283 final PathsListItem nextItem = listItems.get(nextIndex);
284 if (nextItem.isCancelled()) {
285 continue;
286 }
287
288 nextItem.startSearch();
289 }
290 break;
291 }
292 });
293
294
295 listItem.itemTextProperty.addListener((final Observable observable)
296 -> {
297 final int index = listItems.indexOf(listItem);
298 if (index != -1) {
299 listItems.set(index, null);
300 listItems.set(index, listItem);
301 }
302 });
303 listItems.add(listItem);
304 }
305
306
307 pathsList.setItems(listItems);
308 }
309
310 @Override
311 protected void onShown() {
312 final List<PathsListItem> items = pathsList.getItems();
313 if ((items != null) && !items.isEmpty()) {
314 items.get(0).startSearch();
315 }
316 }
317
318
319
320
321 private void cancelSearch() {
322 final List<PathsListItem> items = pathsList.getItems();
323 if (items != null) {
324 items.stream().forEach((item) -> {
325 item.cancelSearch();
326 });
327 }
328 }
329
330 @FXML
331 private void selectButtonOnAction() {
332 cancelSearch();
333
334 final PathsListItem item = pathsList.getSelectionModel().getSelectedItem();
335 result = item == null ? null : item.getPath();
336
337 close();
338 }
339
340 @FXML
341 private void cancelButtonOnAction() {
342 cancelSearch();
343 close();
344 }
345
346 @Override
347 protected boolean onCloseRequest() {
348 cancelSearch();
349 return true;
350 }
351
352 }