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.query.triplePane;
21  
22  import cz.zcu.mre.sparkle.Messages;
23  import cz.zcu.mre.sparkle.data.DataAgent;
24  import cz.zcu.mre.sparkle.data.DataHelper;
25  import cz.zcu.mre.sparkle.gui.dialogs.MessageDialog;
26  import cz.zcu.mre.sparkle.gui.dialogs.query.PathDepthDialog;
27  import cz.zcu.mre.sparkle.gui.dialogs.query.PathsDialog;
28  import cz.zcu.mre.sparkle.gui.dialogs.PropertiesDialog;
29  import cz.zcu.mre.sparkle.gui.dialogs.other.ErrorDialog;
30  import cz.zcu.mre.sparkle.gui.dialogs.other.ProgressDialog;
31  import cz.zcu.mre.sparkle.gui.query.QueryFormPane;
32  import cz.zcu.mre.sparkle.gui.query.autoComplete.AutoCompleteListHandler;
33  import cz.zcu.mre.sparkle.gui.query.autoComplete.PrefixedNamesAutoCompleteListHandler;
34  import cz.zcu.mre.sparkle.gui.query.helpers.*;
35  import cz.zcu.mre.sparkle.gui.query.modifiers.GroupGraphPatternPane;
36  import cz.zcu.mre.sparkle.gui.query.other.TypedTextField;
37  import cz.zcu.mre.sparkle.gui.tools.Components;
38  import cz.zcu.mre.sparkle.gui.tools.ReferenceKeeper;
39  import cz.zcu.mre.sparkle.tools.CancellableConsumer;
40  import cz.zcu.mre.sparkle.tools.Changeable;
41  import cz.zcu.mre.sparkle.tools.Definitions;
42  import cz.zcu.mre.sparkle.tools.Saveable;
43  import cz.zcu.mre.sparkle.tools.sparqlParser.SparqlParserTriple;
44  import cz.zcu.mre.sparkle.tools.sparqlValidation.SparqlValidationUtils;
45  import javafx.beans.InvalidationListener;
46  import javafx.beans.Observable;
47  import javafx.beans.binding.BooleanExpression;
48  import javafx.beans.property.*;
49  import javafx.beans.property.Property;
50  import javafx.beans.value.ChangeListener;
51  import javafx.beans.value.ObservableBooleanValue;
52  import javafx.collections.ListChangeListener.Change;
53  import javafx.collections.ObservableList;
54  import javafx.collections.transformation.SortedList;
55  import javafx.concurrent.Task;
56  import javafx.event.ActionEvent;
57  import javafx.event.EventHandler;
58  import javafx.fxml.FXML;
59  import javafx.scene.Node;
60  import javafx.scene.control.*;
61  import javafx.scene.input.ClipboardContent;
62  import javafx.scene.input.DataFormat;
63  import javafx.scene.input.Dragboard;
64  import javafx.scene.input.TransferMode;
65  import javafx.scene.layout.HBox;
66  import javafx.scene.layout.VBox;
67  import javafx.scene.shape.Path;
68  import javafx.stage.Window;
69  import org.apache.jena.query.Query;
70  import org.apache.jena.query.QuerySolution;
71  import org.apache.jena.rdf.model.*;
72  import org.apache.jena.vocabulary.RDFS;
73  import org.w3c.dom.Document;
74  import org.w3c.dom.Element;
75  import org.w3c.dom.NodeList;
76  import javax.xml.parsers.DocumentBuilderFactory;
77  import javax.xml.parsers.ParserConfigurationException;
78  import javax.xml.transform.TransformerException;
79  import javax.xml.transform.TransformerFactory;
80  import javax.xml.transform.TransformerFactoryConfigurationError;
81  import javax.xml.transform.dom.DOMResult;
82  import javax.xml.transform.dom.DOMSource;
83  import javax.xml.transform.stream.StreamResult;
84  import javax.xml.transform.stream.StreamSource;
85  import java.io.StringReader;
86  import java.io.StringWriter;
87  import java.util.*;
88  import java.util.concurrent.ExecutionException;
89  import java.util.logging.Level;
90  import java.util.logging.Logger;
91  import java.util.regex.Matcher;
92  import static cz.zcu.mre.sparkle.tools.Definitions.DATA_TYPE_PREFIX;
93  import static cz.zcu.mre.sparkle.tools.Definitions.LANG_PREFIX;
94  import static cz.zcu.mre.sparkle.tools.Utils.*;
95  import static cz.zcu.mre.sparkle.tools.sparqlParser.SparqlParserUtils.FIELD_TYPE;
96  
97  /**
98   * Kontroler kontejnerové komponenty představující v dotazu jednu trojici. Může
99   * obsahovat další trojice sdílející s touto stejný subjekt.
100  *
101  * @author Jan Smucr
102  * @author Klara Hlavacova
103  * @author Petr Vcelak (vcelak@kiv.zcu.cz)
104  */
105 public final class TriplePane
106         extends VBox
107         implements VariableConstraintsGenerator, PrefixesUser, VariablesGenerator, VariablesCollector, Saveable,
108         Changeable {
109 
110     private static final Logger LOG = Logger.getLogger(TriplePane.class.getName());
111 
112     private static final String XML_FIELD_TYPE = "type"; //$NON-NLS-1$
113     static final String XML_FIELD_TEXT = "text"; //$NON-NLS-1$
114     static final String XML_LITERAL_DATA_TYPE = "datatype"; //$NON-NLS-1$
115     static final String XML_LITERAL_LANG = "lang"; //$NON-NLS-1$
116     private static final String XML_SUBTRIPLES_ELEMENT_NAME = "Subtriples"; //$NON-NLS-1$
117     static final String XML_OBJECT_ELEMENT_NAME = "Object"; //$NON-NLS-1$
118     public static final String XML_PREDICATE_ELEMENT_NAME = "Predicate"; //$NON-NLS-1$
119     public static final String XML_SUBJECT_ELEMENT_NAME = "Subject"; //$NON-NLS-1$
120     public static final String XML_ELEMENT_NAME = "Triple"; //$NON-NLS-1$
121     public static final DataFormat DRAG_FORMAT = new DataFormat("sparkle.triple"); //$NON-NLS-1$
122     private static final String XML_SUBTRIPLE_ELEMENT_NAME = "Subtriple"; //$NON-NLS-1$
123     private static final int RANDOM_VARIABLE_LENGTH = 12;
124     private static final int BLANK_NODE_LENGTH = 3;
125 
126     private String prefferedlanguage;
127 
128     /**
129      * Klavesove zkratky
130      */
131     private final TriplePaneShortcuts shortcuts;
132 
133     @FXML
134     private HBox subjectFieldContainer;
135     @FXML
136     private HBox propertyFieldContainer;
137     @FXML
138     private HBox objectFieldContainer;
139 
140     @FXML
141     private VBox container;
142 
143     @FXML
144     private TextField langTextField;
145     @FXML
146     private Button addSubTripleButton, moveDownButton, moveUpButton, findPathButton, findPropertiesButton;
147 
148     @FXML
149     private ComboBox<String> objectDataTypeComboBox;
150     @FXML
151     private ContextMenu prefixesContextMenu;
152     @FXML
153     private Path arrow;
154 
155     @FXML
156     private MenuButton literalTypeMenuButton;
157     @FXML
158     private MenuItem plainStringLiteralMenuItem, typedLiteralMenuItem, localizedStringLiteralMenuItem;
159 
160     private BooleanExpression arrowVisibilityCondition;
161     private final ObjectProperty<TriplePane> parentTriplePaneProperty = new SimpleObjectProperty<>(null);
162 
163     private final AutoCompleteListHandler variablesAutoCompleteListHandler, propertiesAutoCompleteListHandler,
164             resourcesAutoCompleteListHandler;
165 
166     private final ObservableList<Node> siblings;
167 
168     private final QueryFormPane<?> parentQueryFormPane;
169     private final GroupGraphPatternPane parentGroupGraphPatternPane;
170     private final Set<VariablesCollector> variablesCollectors = new HashSet<>();
171     private final ObjectProperty<EventHandler<ObjectRelatedActionEvent<TriplePane>>> onRemovalRequested
172             = new SimpleObjectProperty<>();
173 
174     private final ObjectProperty<LiteralType> literalTypeProperty = new SimpleObjectProperty<>();
175 
176     private final ReferenceKeeper objectFieldTypePropertyRefKeeper = new ReferenceKeeper();
177 
178     private final TypedTextField subjectField, propertyField, objectField;
179 
180     private final Set<InvalidationListener> observers = new HashSet<>();
181     private final BooleanProperty enableVariables = new SimpleBooleanProperty();
182     private final BooleanProperty enableBlankNodes = new SimpleBooleanProperty();
183 
184     public TriplePane(final GroupGraphPatternPane parentPane, final ObservableList<Node> siblings,
185             final boolean checkValues, final boolean enableVariables, final boolean enableBlankNodes) {
186         this(parentPane, siblings, null, checkValues, enableVariables, enableBlankNodes);
187     }
188 
189     public TriplePane(final GroupGraphPatternPane parentPane, final ObservableList<Node> siblings,
190             final TriplePane parentTriplePane, final boolean checkValues, final boolean enableVariables,
191             final boolean enableBlankNodes) {
192         Components.load(this);
193 
194         shortcuts = new TriplePaneShortcuts(this);
195 
196         this.parentGroupGraphPatternPane = parentPane;
197         this.parentQueryFormPane = parentPane.getParentQueryFormPane();
198         this.siblings = siblings;
199         this.enableVariables.set(enableVariables);
200         this.enableBlankNodes.set(enableBlankNodes);
201         parentTriplePaneProperty.set(parentTriplePane);
202 
203         // Automatické sledování změn u vnořených trojic
204         autoWatch(container.getChildren(), TriplePane.class);
205 
206         if (siblings != null) {
207             siblings.addListener(this::onPanePositionChanged);
208         }
209 
210         makeInvisibleUnmanaged(container, objectDataTypeComboBox, literalTypeMenuButton, langTextField, findPathButton,
211                 findPropertiesButton);
212 
213         setupContainer();
214 
215         variablesAutoCompleteListHandler = parentQueryFormPane.DEFAULT_VARIABLES_AUTOCOMPLETE_LIST_HANDLER;
216 
217         propertiesAutoCompleteListHandler
218                 = new PrefixedNamesAutoCompleteListHandler(parentQueryFormPane, true, false, this);
219 
220         resourcesAutoCompleteListHandler
221                 = new PrefixedNamesAutoCompleteListHandler(parentQueryFormPane.getQueryResourcesStorage(), false, true);
222 
223         subjectField
224                 = SubjectField.initSubjectField(this, parentTriplePane, checkValues, enableVariables, enableBlankNodes);
225         propertyField = PropertyField.initPropertyField(this, checkValues, enableVariables);
226         objectField = ObjectField.setupObjectField(this, checkValues, enableVariables, enableBlankNodes);
227 
228         //  PropertyField.addPropertyFieldTextChangeListener(this);//......
229         initButtons();
230 
231         addArrowVisiblePropertyBinding(hoverProperty().or(
232                 subjectField.textFieldFocusedProperty().or(propertyField.textFieldFocusedProperty())
233                         .or(objectField.textFieldFocusedProperty())));
234 
235         //parentQueryFormPane.getQueryDataTypesStorage().addListener(objectFieldTypePropertyRefKeeper.toWeak((term, added)
236         // -> onDataTypesStorageChanged(term, added)));
237         // Reakce na update seznamu datových typů
238         parentQueryFormPane.getQueryDataTypesStorage().addListener(
239                 objectFieldTypePropertyRefKeeper.toWeak((term, added) -> updateDataTypeComboBox()));
240 
241         // Sledování změn
242         setupWatch();
243         setupDrag();
244     }
245 
246     /**
247      *
248      */
249     private void initButtons() {
250         addSubTripleButton.disableProperty().bind(parentTriplePaneProperty.isNotNull());
251         findPathButton
252                 .visibleProperty()
253                 .bind(parentTriplePaneProperty
254                         .isNull()
255                         .and(propertyField.fieldTypeProperty().isEqualTo(FieldType.PrefixedName)
256                                 .or(propertyField.fieldTypeProperty().isEqualTo(FieldType.IRI)))
257                         .and(subjectField.fieldTypeProperty().isEqualTo(FieldType.Variable)));
258         findPathButton.disableProperty().bind(subjectField.fieldContentIsValidProperty().not()
259                 .or(propertyField.fieldContentIsValidProperty().not()));
260         findPropertiesButton.visibleProperty()
261                 .bind(propertyField.fieldTypeProperty().isEqualTo(FieldType.PrefixedName));
262         findPropertiesButton.disableProperty().bind(subjectField.fieldContentIsValidProperty().not());
263     }
264 
265     /**
266      *
267      */
268     private void setupWatch() {
269         watch(subjectField);
270         watch(propertyField);
271         watch(objectField);
272         watch(literalTypeProperty);
273         watch(objectDataTypeComboBox.valueProperty());
274     }
275 
276     /**
277      *
278      */
279     private void setupDrag() {
280         setOnDragDetected((event) -> {
281             final Dragboard dragboard = startDragAndDrop(TransferMode.MOVE);
282             final ClipboardContent content = new ClipboardContent();
283             try {
284                 content.put(DRAG_FORMAT, toXML());
285                 dragboard.setContent(content);
286             } catch (final TransformerException | ParserConfigurationException e) {
287                 LOG.log(Level.SEVERE, "Exception: ", e); //$NON-NLS-1$
288             }
289             event.consume();
290         });
291 
292         setOnDragDone((event) -> {
293             if (event.getTransferMode() == TransferMode.MOVE) {
294                 removeButtonOnAction(new ActionEvent());
295             }
296             event.consume();
297         });
298     }
299 
300     /**
301      *
302      */
303     private void setupContainer() {
304         container.getChildren().addListener(
305                 (final Observable observable) -> container.setVisible(container.getChildren().size() > 0));
306 
307         container.setVisible(container.getChildren().size() > 0);
308     }
309 
310     /**
311      *
312      */
313     void updateDataTypeComboBox() {
314         final ObservableList<String> items
315                 = new SortedList<>(parentQueryFormPane.getQueryDataTypesStorage().getAllDataTypes(),
316                         String.CASE_INSENSITIVE_ORDER);
317         if (objectDataTypeComboBox.itemsProperty().isBound()) {
318             return;
319         }
320 
321         final String selectedItem = objectDataTypeComboBox.getValue();
322 
323         objectDataTypeComboBox.setItems(items);
324 
325         if (items.isEmpty()) {
326             return;
327         }
328 
329         if (objectDataTypeComboBox.valueProperty().isBound()) {
330             return;
331         }
332 
333         final int selectedItemIndex = items.indexOf(selectedItem);
334         if ((selectedItem != null) && (selectedItemIndex != -1)) {
335             objectDataTypeComboBox.setValue(selectedItem);
336             return;
337         }
338 
339         objectDataTypeComboBox.setValue(objectDataTypeComboBox.getItems().get(0));
340     }
341 
342     /**
343      *
344      * @return Get arrow as the Path instance.
345      */
346     public final Path getArrow() {
347         return arrow;
348     }
349 
350     private void addArrowVisiblePropertyBinding(final BooleanExpression property) {
351         if (arrowVisibilityCondition == null) {
352             arrowVisibilityCondition = property;
353         } else {
354             arrowVisibilityCondition = arrowVisibilityCondition.or(property);
355         }
356 
357         arrow.visibleProperty().bind(arrowVisibilityCondition);
358     }
359 
360     /**
361      *
362      * @param checkValues Check values when adding triples.
363      * @return initialized Triple Pane.
364      */
365     public TriplePane addSubTriple(boolean checkValues) {
366         final ObservableList<Node> containerChildren = container.getChildren();
367         final TriplePane subTriplePane
368                 = new TriplePane(parentGroupGraphPatternPane, containerChildren, this, checkValues, enableVariables.get(),
369                         enableBlankNodes.get());
370         subTriplePane.addVariablesCollector(this);
371         containerChildren.add(subTriplePane);
372         addArrowVisiblePropertyBinding(subTriplePane.getArrow().visibleProperty());
373 
374         subTriplePane.setOnRemovalRequested(
375                 objectFieldTypePropertyRefKeeper
376                         .toWeak((final ObjectRelatedActionEvent<TriplePane> e) -> removeTriple(e.relatedObject)));
377 
378         return subTriplePane;
379     }
380 
381     /**
382      *
383      */
384     private void removeTriple(final TriplePane triplePane) {
385         container.getChildren().remove(triplePane);
386         final Set<String> variablesToRemove = triplePane.getVariables();
387         variablesToRemove.removeAll(getVariables());
388         variablesToRemove.forEach(this::notifyVariableRemoved);
389     }
390 
391     /**
392      *
393      * @param event
394      */
395     @FXML
396     protected void addSubTripleButtonOnAction(final ActionEvent event) {
397         addSubTriple(true).requestFocus();
398     }
399 
400     /**
401      *
402      */
403     @FXML
404     void setPlainStringLiteralType() {
405         literalTypeMenuButton.setText(plainStringLiteralMenuItem.getText());
406         literalTypeProperty.set(LiteralType.PlainString);
407     }
408 
409     /**
410      *
411      */
412     @FXML
413     void setTypedLiteralType() {
414         literalTypeMenuButton.setText(typedLiteralMenuItem.getText());
415         literalTypeProperty.set(LiteralType.Typed);
416     }
417 
418     /**
419      *
420      */
421     @FXML
422     void setLocalizedStringLiteralType() {
423         literalTypeMenuButton.setText(localizedStringLiteralMenuItem.getText());
424         literalTypeProperty.set(LiteralType.LocalizedString);
425     }
426 
427     /**
428      *
429      */
430     @FXML
431     private void removeButtonOnAction(final ActionEvent event) {
432         if (onRemovalRequested.get() != null) {
433             onRemovalRequested.get().handle(new ObjectRelatedActionEvent<>(event.getSource(), event.getTarget(), this));
434         }
435     }
436 
437     /**
438      *
439      * @return object property on removal request.
440      */
441     public final ObjectProperty<EventHandler<ObjectRelatedActionEvent<TriplePane>>> onRemovalRequestedProperty() {
442         return onRemovalRequested;
443     }
444 
445     /**
446      *
447      * @param handler set event handler fir on remove property event.
448      */
449     public final void setOnRemovalRequested(final EventHandler<ObjectRelatedActionEvent<TriplePane>> handler) {
450         onRemovalRequested.set(handler);
451     }
452 
453     /**
454      *
455      * @return get event handler for on removal request event.
456      */
457     public final EventHandler<ObjectRelatedActionEvent<TriplePane>> getOnRemovalRequested() {
458         return onRemovalRequested.get();
459     }
460 
461     /**
462      *
463      */
464     @FXML
465     private void moveUpButtonOnAction(final ActionEvent event) {
466         if (siblings == null) {
467             return;
468         }
469         final int currentPosition = siblings.indexOf(this);
470         if (currentPosition > 0) {
471             siblings.remove(currentPosition);
472             siblings.add(currentPosition - 1, this);
473         }
474     }
475 
476     /**
477      *
478      */
479     @FXML
480     private void moveDownButtonOnAction(final ActionEvent event) {
481         if (siblings == null) {
482             return;
483         }
484         final int currentPosition = siblings.indexOf(this);
485         if ((currentPosition > -1) && (currentPosition < (siblings.size() - 1))) {
486             siblings.remove(currentPosition);
487             siblings.add(currentPosition + 1, this);
488         }
489     }
490 
491     /**
492      *
493      */
494     private void onPanePositionChanged(final Change<? extends Node> change) {
495         final ObservableList<? extends Node> list = change.getList();
496         final int index = list.indexOf(this);
497         if (index >= 0) {
498             moveUpButton.setDisable(index == 0);
499             moveDownButton.setDisable((index + 1) >= list.size());
500         }
501     }
502 
503     /**
504      *
505      */
506     @Override
507     public final String getQueryPart() {
508         final StringBuilder sb = new StringBuilder();
509 
510         if (getParentTriplePane() == null) {
511             sb.append(subjectField.getValue());
512             sb.append(' ');
513         }
514 
515         sb.append(propertyField.getValue());
516         sb.append(' ');
517 
518         sb.append(objectField.getValue());
519         sb.append(' ');
520 
521         if (objectField.getFieldType() == FieldType.Literal) {
522             switch (literalTypeProperty.get()) {
523                 case Typed:
524                     final String dataType = objectDataTypeComboBox.getValue();
525                     if ((dataType != null) && !dataType.isEmpty()) {
526                         sb.append(literalTypeMenuButton.getText());
527                         sb.append(dataType);
528                     }
529                     break;
530 
531                 case LocalizedString:
532                     final String lang = langTextField.getText().trim();
533                     if (!lang.isEmpty()) {
534                         sb.append(literalTypeMenuButton.getText());
535                         sb.append(lang);
536                     }
537                     break;
538 
539                 default:
540                     break;
541             }
542 
543         }
544 
545         sb.append(' ');
546 
547         if (getParentTriplePane() != null) {
548             return sb.toString();
549         }
550 
551         //final String[] predicateField = {getPropertyField().getValue()};
552         container.getChildren().stream().filter((node) -> (node instanceof TriplePane)).forEach((node) -> {
553             sb.append("; "); //$NON-NLS-1$
554 
555             String queryPart = ((TriplePane) node).getQueryPart();
556 
557             /*if(!predicateField[0].isEmpty() && queryPart.startsWith(predicateField[0])){
558                 sb.replace(sb.lastIndexOf(";"), sb.lastIndexOf(";")+1,",");
559                 queryPart = queryPart.replaceFirst(predicateField[0], "");
560             }*/
561             sb.append(queryPart);
562             // predicateField[0] = ((TriplePane) node).getPropertyField().getValue();
563         });
564 
565         sb.append(". "); //$NON-NLS-1$
566 
567         return sb.toString();
568     }
569 
570     /**
571      *
572      */
573     @Override
574     public final Set<String> getPrefixesUsed(final boolean appendDelimiter) {
575         final HashSet<String> prefixes = new HashSet<>();
576         prefixes.addAll(subjectField.getPrefixesUsed(appendDelimiter));
577         prefixes.addAll(propertyField.getPrefixesUsed(appendDelimiter));
578         prefixes.addAll(objectField.getPrefixesUsed(appendDelimiter));
579 
580         if ((objectField.getFieldType() == FieldType.Literal) && (literalTypeProperty.get() == LiteralType.Typed)) {
581             final String type = objectDataTypeComboBox.getValue();
582             if ((type != null) && !type.isEmpty()) {
583                 final String prefix = extractPrefix(type);
584                 if ((prefix != null) && !prefix.isEmpty()) {
585                     prefixes.add(appendDelimiter ? prefix + Definitions.PREFIXED_NAME_PREFIX_DELIMITER : prefix);
586                 }
587             }
588         }
589 
590         container.getChildren().stream().filter((node) -> (node instanceof PrefixesUser)).forEach((node) -> prefixes.addAll(((PrefixesUser) node).getPrefixesUsed(false)));
591 
592         return prefixes;
593     }
594 
595     /**
596      *
597      */
598     @Override
599     public final Set<String> getVariables() {
600         final Set<String> result = new HashSet<>();
601         result.addAll(subjectField.getVariables());
602         result.addAll(propertyField.getVariables());
603         result.addAll(objectField.getVariables());
604 
605         container.getChildren().stream().filter((node) -> (node instanceof SimpleVariablesGenerator))
606                 .forEach((node) -> result.addAll(((SimpleVariablesGenerator) node).getVariables()));
607         return result;
608     }
609 
610     @Override
611     public final void onVariableAdded(final String variableName) {
612         notifyVariableAdded(variableName);
613     }
614 
615     @Override
616     public final void onVariableRemoved(final String variableName) {
617         notifyVariableRemoved(variableName);
618     }
619 
620     @Override
621     public final void notifyVariableRemoved(final String variableName) {
622         if (getVariables().contains(variableName)) {
623             return;
624         }
625         variablesCollectors.stream().forEach((collector) -> collector.onVariableRemoved(variableName));
626     }
627 
628     @Override
629     public final void notifyVariableAdded(final String variableName) {
630         variablesCollectors.stream().forEach((collector) -> collector.onVariableAdded(variableName));
631     }
632 
633     @Override
634     public final void addVariablesCollector(final VariablesCollector collector) {
635         variablesCollectors.add(collector);
636     }
637 
638     @Override
639     public final void removeVariablesCollector(final VariablesCollector collector) {
640         variablesCollectors.remove(collector);
641     }
642 
643     @Override
644     public final void onVariableChanged(final String oldVariableName, final String newVariableName) {
645         notifyVariableChanged(oldVariableName, newVariableName);
646     }
647 
648     @Override
649     public final void notifyVariableChanged(final String oldVariableName, final String newVariableName) {
650         if (getVariables().contains(oldVariableName)) {
651             variablesCollectors.stream().forEach((collector) -> collector.onVariableAdded(newVariableName));
652         } else {
653             variablesCollectors.stream().forEach((collector) -> collector.onVariableChanged(oldVariableName, newVariableName));
654         }
655     }
656 
657     @Override
658     public final void requestFocus() {
659         if (getParentTriplePane() == null) {
660             subjectField.requestFocus();
661         } else {
662             propertyField.requestFocus();
663         }
664     }
665 
666     @FXML
667     private void dataTypeComboBoxOnShowing() {
668         updateDataTypeComboBox();
669     }
670 
671     public TriplePane getParentTriplePane() {
672         return parentTriplePaneProperty.get();
673     }
674 
675     private Window getWindow() {
676         return parentQueryFormPane.getMainForm().getMainTabPane().getScene().getWindow();
677     }
678 
679     private void saveDepth(int depth) {
680         this.parentQueryFormPane.getMainForm().setDepth(depth);
681     }
682 
683     /**
684      * It obtains a required depth of searching properties.
685      *
686      * @return The depth of searching properties.
687      */
688     private int getDepth() {
689         int depth = 0;
690         while (depth < 1) {
691             try {
692                 final PathDepthDialog pathDepthDialog = PathDepthDialog
693                         .open(getWindow(), Messages.getString("PATH_DEPTH"),
694                                 Integer.toString(
695                                         this.parentQueryFormPane.getMainForm().getDepth())); //$NON-NLS-1$ //$NON-NLS-2$
696 
697                 if (pathDepthDialog == null || pathDepthDialog.getResult() == null) {
698                     return 0;
699                 }
700                 String depthString = pathDepthDialog.getResult();
701                 prefferedlanguage = pathDepthDialog.getPreferredLanguage();
702 
703                 if (depthString.equals(Definitions.BLANK_NODE_PREFIX)) {
704                     return depth;
705                 }
706                 final int obtainedNumber = Integer.parseInt(depthString);
707                 if (obtainedNumber > depth) {
708                     depth = obtainedNumber;
709                 } else {
710                     throw new NumberFormatException();
711                 }
712             } catch (NumberFormatException e) {
713                 MessageDialog.open(getWindow(), Messages.getString("WARNING"),
714                         Messages.getString("INSERT_INTEGER")); //$NON-NLS-1$ //$NON-NLS-2$
715             }
716         }
717         saveDepth(depth);
718 
719         return depth;
720     }
721 
722     /**
723      *
724      */
725     @FXML
726     private void findPropertiesButtonOnAction() {
727         int obtainedDepth = getDepth();
728         if (obtainedDepth < 1) {
729             return;
730         }
731         final int depth = obtainedDepth + 1;
732 
733         final SimpleBooleanProperty cancelled = new SimpleBooleanProperty(false);
734         final List<String> paths;
735         try {
736             paths = ProgressDialog.performTask(getWindow(), new Task<List<String>>() {
737                 @Override
738                 protected final List<String> call() throws Exception {
739                     updateMessage(Messages.getString("SEARCHING_PATHS")); //$NON-NLS-1$
740                     List<RDFNode> modelNodes = getWantedGraphElement(cancelled);
741                     if (modelNodes.isEmpty()) {
742                         return null;
743                     }
744                     List<String> paths = new ArrayList<>();
745                     modelNodes.stream().forEach((modelNode) -> paths.addAll(
746                             getPaths(parentQueryFormPane.getDataAgent().getFullModel(), depth,
747                                     modelNode.asResource(), prefferedlanguage,
748                                     cancelled)));
749                     if (cancelled.get() || paths.isEmpty()) {
750                         return null;
751                     }
752                     // It sets unique records
753                     Set<String> pathSet = new HashSet<>(paths);
754                     paths.clear();
755                     paths.addAll(pathSet);
756                     return paths;
757                 }
758 
759                 @Override
760                 protected final void cancelled() {
761                     cancelled.set(true);
762                 }
763             });
764         } catch (InterruptedException | ExecutionException e) {
765             ErrorDialog
766                     .open(getScene().getWindow(), Messages.getString("ERROR"), Messages.getString("OPERATION_FAILED"),
767                             e); //$NON-NLS-1$ //$NON-NLS-2$
768             LOG.log(Level.SEVERE, "Exception: ", e); //$NON-NLS-1$
769             return;
770         }
771         if (cancelled.get()) {
772             return;
773         } else if (paths == null || paths.isEmpty()) {
774             MessageDialog.open(getWindow(), Messages.getString("WARNING"),
775                     Messages.getString("NO_RESULTS_FOUND")); //$NON-NLS-1$ //$NON-NLS-2$
776             return;
777         }
778 
779         String selectedPath = PropertiesDialog.open(getWindow(), paths);
780         if (selectedPath != null) {
781             setSelectedPath(selectedPath);
782         }
783 
784         findPropertiesButton.requestFocus();
785     }
786 
787     /**
788      *
789      */
790     private void setObjectFieldType(FieldType fieldType, String objectFieldText) {
791         objectField.setFieldType(fieldType);
792         objectField.setText(objectFieldText);
793     }
794 
795     /**
796      * The method takes a selected path (from {@link PropertiesDialog}) and sets
797      * it into text fields.
798      *
799      * @param selectedPath A selected path.
800      */
801     private void setSelectedPath(String selectedPath) {
802         Matcher m = FOUND_PATHS.matcher(selectedPath);
803         if (m.matches()) {
804             propertyField.setText(m.group(1)); // Found path
805             String objectFieldText = m.group(2);
806             String firstCharOfObject = objectFieldText.substring(0, 1);
807             switch (firstCharOfObject) {
808                 case Definitions.BLANK_NODE_PREFIX:
809                     setObjectFieldType(FieldType.Variable, objectFieldText);
810                     notifyVariableAdded(objectFieldText);
811                     break;
812                 case Definitions.QUOTATION_MARK:
813                     m = OBJECT_LITERAL.matcher(objectFieldText);
814                     if (m.matches()) {
815                         setObjectFieldType(FieldType.Literal, m.group(1));
816                         String notPlainStringMark = m.group(2); // "@" or "^^"
817                         String literalDescription = m.group(3); // Language or RDF data type
818                         if (notPlainStringMark != null && !notPlainStringMark.isEmpty()
819                                 && literalDescription != null && !literalDescription.isEmpty()) {
820                             if (notPlainStringMark.equals(DATA_TYPE_PREFIX)) {
821                                 objectDataTypeComboBox.setValue(literalDescription);
822                                 setTypedLiteralType();
823                             } else if (notPlainStringMark.equals(LANG_PREFIX)) {
824                                 langTextField.setText(literalDescription);
825                                 setLocalizedStringLiteralType();
826                             }
827                         } else {
828                             setPlainStringLiteralType();
829                         }
830                     }
831                     break;
832                 default:
833                     if (SparqlValidationUtils.isPrefixedNameValid(objectFieldText, true)) {
834                         if (parentQueryFormPane.getQueryPrefixesStorage()
835                                 .getNsPrefixURI(objectFieldText.substring(0, objectFieldText
836                                         .indexOf(Definitions.PREFIXED_NAME_PREFIX_DELIMITER))) != null) {
837                             setObjectFieldType(FieldType.PrefixedName, objectFieldText);
838                         } else {
839                             setObjectFieldType(FieldType.Literal, objectFieldText);
840                         }
841                     } else {
842                         setObjectFieldType(FieldType.IRI,
843                                 parentQueryFormPane.getQueryPrefixesStorage().shortForm(objectFieldText));
844                     }
845                     break;
846             }
847         }
848     }
849 
850     /**
851      * It returns a queried graph element that is used to search paths.
852      *
853      * @param cancelled Boolean variable - if it is <code>true</code> then this
854      * operation is terminated.
855      *
856      * @return A queried graph element.
857      */
858     private List<RDFNode> getWantedGraphElement(SimpleBooleanProperty cancelled) {
859         final String subjectText = this.subjectField.getText();
860         final List<RDFNode> results = new ArrayList<>();
861         switch (this.subjectField.getFieldType()) {
862             case Variable:
863             case BlankNode:
864                 String requiredVariable = Definitions.VARIABLE_PREFIX + this.subjectField.getText();
865                 results.addAll(getVariableNode(createQuery(requiredVariable), requiredVariable, cancelled));
866                 break;
867             case PrefixedName:
868                 results.add(parentQueryFormPane.getDataAgent().getFullModel().getResource(
869                         this.parentQueryFormPane.getQueryPrefixesStorage().expandPrefix(subjectText)));
870                 break;
871             case IRI:
872                 results.add(parentQueryFormPane.getDataAgent().getFullModel().getResource(subjectText));
873                 break;
874             default:
875                 LOG.info(this.getClass().getCanonicalName() + ": Unknown field type");
876                 break;
877         }
878         return results;
879     }
880 
881     /**
882      * It obtains a wanted graph element from storage.
883      *
884      * @param query A composed SPARQL query to obtain the wanted graph element.
885      * @param requiredVariable The wanted graph element.
886      * @param cancelled Boolean variable - if it is <code>true</code> then this
887      * operation is terminated.
888      *
889      * @return <code>RDFNode</code> element.
890      */
891     private List<RDFNode> getVariableNode(String query, String requiredVariable,
892             final ObservableBooleanValue cancelled) {
893         final List<RDFNode> results = new ArrayList<>();
894         final CancellableConsumer<QuerySolution> consumer = new CancellableConsumer<QuerySolution>() {
895             @Override
896             public void accept(final QuerySolution qs) {
897                 if (qs.contains(requiredVariable)) {
898                     results.add(qs.get(requiredVariable));
899                 }
900             }
901         };
902         final ChangeListener<Boolean> cancellationListener = (observable, oldValue, newValue) -> {
903             if (newValue) {
904                 consumer.cancel();
905             }
906         };
907 
908         cancelled.addListener(cancellationListener);
909         parentQueryFormPane.getDataAgent().select(query, consumer);
910         cancelled.removeListener(cancellationListener);
911         return results;
912     }
913 
914     /**
915      * The method forms a query that is used to obtain a wanted graph element.
916      *
917      * @param requiredVariable
918      *
919      * @return Final form of query.
920      */
921     private String createQuery(final String requiredVariable) {
922         final String currentTriplePane = this.getQueryPart();
923         final String groupGraphPatternPanePart
924                 = getGroupGraphPattPaneFromQuery(parentQueryFormPane.getQuery(), currentTriplePane);
925         int indexOfTriplePane = groupGraphPatternPanePart.indexOf(currentTriplePane);
926         String firstPartOfWhereClause = groupGraphPatternPanePart.substring(0, indexOfTriplePane).trim();
927         if (firstPartOfWhereClause.endsWith(";")) { //$NON-NLS-1$
928             firstPartOfWhereClause
929                     = firstPartOfWhereClause.substring(0, firstPartOfWhereClause.length() - 1) + "."; //$NON-NLS-1$
930         }
931         // Query forming
932         final StringBuilder sb = new StringBuilder(" SELECT "); //$NON-NLS-1$
933         sb.append(requiredVariable).append(" WHERE ").append(firstPartOfWhereClause)//$NON-NLS-1$
934                 .append(requiredVariable).
935                 append(Definitions.SPACE).append(Definitions.VARIABLE_PREFIX)
936                 .append(getRandomString(RANDOM_VARIABLE_LENGTH)).
937                 append(" ?").append(getRandomString(RANDOM_VARIABLE_LENGTH)). //$NON-NLS-1$
938                 append(
939                         groupGraphPatternPanePart.substring(indexOfTriplePane + currentTriplePane.length()));
940         // Adding of prefixes
941         sb.insert(0, getUsedQueryPrefixes(parentQueryFormPane.getQueryPrefixesStorage().getNsPrefixMap(), sb.toString()));
942         return sb.toString();
943     }
944 
945     /**
946      * It obtains all paths of the required graph element.
947      *
948      * @param model RDF storage.
949      * @param depth Depht of graph searching.
950      * @param rootNode Initial node of searching.
951      * @param selectedLanguage Prefered language of path nodes.
952      * @param cancelled Boolean variable - if it is <code>true</code> then this
953      * operation is terminated.
954      *
955      * @return List that containing all paths of the required graph element.
956      */
957     private List<String> getPaths(final Model model, final int depth, final Resource rootNode,
958             String selectedLanguage, final SimpleBooleanProperty cancelled) {
959 
960         final List<String> results = new ArrayList<>(); // Found paths.
961         final Map<Resource, String> predicates
962                 = new HashMap<>(); // It stores used prefixed names that are used during searching.
963         final Queue<List<Resource>> queue = new LinkedList<>(); // Queue of lists of elements of path.
964         final List<Resource> rootNodePath = new LinkedList<>(); // It stores all of elements of path.
965         final List<RDFNode> visitedNodes = new LinkedList<>(); // A found grapht elements.
966         final int allowedPathDepth = depth - 1;
967 
968         visitedNodes.add(rootNode);
969         predicates.put(rootNode, Definitions.EMPTY_STRING);
970         rootNodePath.add(rootNode);
971         queue.add(rootNodePath);
972 
973         while (!queue.isEmpty() && !cancelled.get()) {
974             List<Resource> pathNodes = queue.poll();
975 
976             if (pathNodes != null && pathNodes.size() < depth) {
977                 Resource lastPathNode = pathNodes.get(pathNodes.size() - 1);
978                 StmtIterator stmtIterator = model.listStatements(lastPathNode, null, (RDFNode) null);
979 
980                 while (stmtIterator.hasNext()) {
981                     final Statement stmtValue = stmtIterator.next();
982                     final RDFNode objectOfStmtValue = stmtValue.getObject();
983 
984                     if (!visitedNodes.contains(objectOfStmtValue)) {
985                         visitedNodes.add(objectOfStmtValue);
986                         final String prefixedNameOfChild
987                                 = parentQueryFormPane.getQueryPrefixesStorage()
988                                         .shortForm(stmtValue.getPredicate().getURI());
989                         searchAndAddPath(results, allowedPathDepth, pathNodes, predicates, prefixedNameOfChild,
990                                 objectOfStmtValue, selectedLanguage);
991                         if (objectOfStmtValue.isResource()) {
992                             final Resource stmtObjectResource = objectOfStmtValue.asResource();
993                             final List<Resource> foundNodePath = new ArrayList<>(pathNodes);
994                             foundNodePath.add(stmtObjectResource);
995                             queue.add(foundNodePath);
996                             predicates.put(stmtObjectResource, prefixedNameOfChild);
997                         }
998                     }
999                 }
1000             }
1001         }
1002         return results;
1003     }
1004 
1005     /**
1006      * It adds the found path into the list <code>recordsToView</code> that will
1007      * be displayed in {@link PropertiesDialog}.
1008      *
1009      * @param recordsToView List of found paths.
1010      * @param allowedPathDepth Depth of searching.
1011      * @param pathNodes Elements of the found path.
1012      * @param predicates Prefixed names.
1013      * @param lastPrefixedName Last prefixed name of the found path.
1014      * @param objectOfStmtValue Graph element of the found path.
1015      * @param selectedLanguage Prefered language of path nodes.
1016      */
1017     private void searchAndAddPath(final List<String> recordsToView, final int allowedPathDepth,
1018             final List<Resource> pathNodes, final Map<Resource, String> predicates,
1019             final String lastPrefixedName, final RDFNode objectOfStmtValue,
1020             final String selectedLanguage) {
1021 
1022         final StringBuilder sb = new StringBuilder();
1023         int slashCount = 0;
1024 
1025         slashCount
1026                 = pathNodes.stream().map(predicates::get).filter((edge) -> (edge.length() > 0))
1027                         .map((edge) -> {
1028                             sb.append(edge).append("/"); //$NON-NLS-1$
1029                             return edge;
1030                         }).map((_item) -> 1).reduce(slashCount, Integer::sum);
1031 
1032         if (slashCount < allowedPathDepth) {
1033             sb.append(lastPrefixedName).append(" -- "); //$NON-NLS-1$
1034             if (objectOfStmtValue.isLiteral()) { // Literal
1035                 Literal literal = objectOfStmtValue.asLiteral();
1036                 String literalType
1037                         = getLiteralType(this.parentQueryFormPane.getQueryPrefixesStorage(), literal,
1038                                 selectedLanguage);
1039                 if (literalType != null) {
1040                     sb.append(Definitions.QUOTATION_MARK).append(literal.getString()).append(Definitions.QUOTATION_MARK)
1041                             .append(literalType);
1042                 } else {
1043                     return;
1044                 }
1045             } else if (objectOfStmtValue.isAnon()) { // Blank node
1046                 sb.append(Messages.getString("_BLANK")).append(getRandomString(BLANK_NODE_LENGTH)); //$NON-NLS-1$
1047             } else if (objectOfStmtValue.isURIResource()) { // Prefixed name or IRI
1048                 sb.append(parentQueryFormPane.getQueryPrefixesStorage().shortForm(objectOfStmtValue.toString()));
1049             }
1050             recordsToView.add(sb.toString());
1051         }
1052     }
1053 
1054     /**
1055      * Hledání cesty.
1056      */
1057     @FXML
1058     private void findPathButtonOnAction() {
1059         // Získání plného identifikátoru vlastnosti na konci cesty
1060         String endPropertyIRI = null;
1061         if (propertyField.getFieldType() == FieldType.PrefixedName) {
1062             final String fieldContent = propertyField.getText();
1063             final Set<String> prefixes = propertyField.getPrefixesUsed(false);
1064             if (!prefixes.isEmpty()) {
1065                 final String prefix = prefixes.iterator().next();
1066                 endPropertyIRI = parentQueryFormPane.getQueryPrefixesStorage().getPrefixToIri().get(prefix);
1067             }
1068             if (endPropertyIRI == null) {
1069                 ErrorDialog.open(getScene().getWindow(), Messages.getString("ERROR"),
1070                         Messages.getString("PROPERTY_PREFIX_CANNOT_BE_RESOLVED")); //$NON-NLS-1$ //$NON-NLS-2$
1071                 propertyField.requestFocus();
1072                 return;
1073             }
1074             final String localName
1075                     = fieldContent.substring(fieldContent.indexOf(Definitions.PREFIXED_NAME_PREFIX_DELIMITER) + 1);
1076             endPropertyIRI += localName;
1077         }
1078 
1079         /*
1080          * Identifikace začátků cesty:
1081          * - Z dotazu: ze zmínek o typu proměnné.
1082          * - Z úložiště: Z dotazu se extrahují relevantní části a sestaví se nový dotaz,
1083          *               kterým se zjistí typ dat, která může proměnná obsahovat
1084          */
1085         final Set<Resource> startResources;
1086         try {
1087             startResources = ProgressDialog.performTask(getScene().getWindow(), new Task<Set<Resource>>() {
1088                 @Override
1089                 protected final Set<Resource> call() throws Exception {
1090                     // TODO Udělat zrušitelné
1091 
1092                     updateMessage(Messages.getString("RETRIEVING_PATH_START_ELEMENTS")); //$NON-NLS-1$
1093                     return identifyVariable(subjectField.getText().trim(), 0, 0, null, true);
1094                 }
1095             });
1096         } catch (InterruptedException | ExecutionException e) {
1097             ErrorDialog
1098                     .open(getScene().getWindow(), Messages.getString("ERROR"), Messages.getString("OPERATION_FAILED"),
1099                             e); //$NON-NLS-1$ //$NON-NLS-2$
1100             LOG.log(Level.SEVERE, "Exception: ", e); //$NON-NLS-1$
1101             return;
1102         }
1103 
1104         // Zrušeno
1105         if (startResources == null) {
1106             return;
1107         }
1108 
1109         if (startResources.isEmpty()) {
1110             MessageDialog.open(getScene().getWindow(), Messages.getString("SORRY"),
1111                     Messages.getString("NO_PATH_START_ELEMENTS_AVAILIBLE")); //$NON-NLS-1$ //$NON-NLS-2$
1112             return;
1113         }
1114 
1115         // Otevření dialogu a samotné vyhledání cest
1116         final LinkedList<Resource> path = PathsDialog
1117                 .open(getScene().getWindow(), parentQueryFormPane.getDataAgent(),
1118                         parentQueryFormPane.getQueryPrefixesStorage(),
1119                         startResources, ResourceFactory.createProperty(endPropertyIRI));
1120         if (path == null) {
1121             return;
1122         }
1123 
1124         final FieldType objectFieldType = objectField.getFieldType();
1125         final String objectContent = objectField.getText();
1126 
1127         // Typ počáteční proměnné - nepotřebujeme
1128         path.removeFirst();
1129 
1130         // Vytvoření nových trojic "po cestě"
1131         final Iterator<Resource> i = path.iterator();
1132         TriplePane currentTriple = this;
1133         while (i.hasNext()) {
1134             final Resource node = i.next();
1135             if (node instanceof Property) {
1136                 if (node.isAnon()) {
1137                     final String varName
1138                             = parentQueryFormPane
1139                                     .generateNewVariableName(Messages.getString("_BLANK"), null); //$NON-NLS-1$
1140                     notifyVariableAdded(varName);
1141                     currentTriple.propertyField.setFieldType(FieldType.Variable);
1142                     currentTriple.propertyField.setText(varName);
1143                 } else {
1144                     final String nodeID = node.toString();
1145                     final String prefixedName = parentQueryFormPane.getQueryPrefixesStorage().shortForm(nodeID);
1146                     currentTriple.propertyField
1147                             .setFieldType(nodeID.equals(prefixedName) ? FieldType.IRI : FieldType.PrefixedName);
1148                     currentTriple.propertyField.setText(prefixedName);
1149                 }
1150                 continue;
1151             }
1152 
1153             final String varName
1154                     = parentQueryFormPane.generateNewVariableName(Messages.getString("_PATH"), null); //$NON-NLS-1$
1155 
1156             /*
1157              * Toto by bývalo nebylo nutné, ale zdá se, že je,
1158              * protože nejsou nové proměnné zaregistrovány včas
1159              * a generují se tak stále stejné názvy.
1160              */
1161             notifyVariableAdded(varName);
1162 
1163             currentTriple.objectField.setFieldType(FieldType.Variable);
1164             currentTriple.objectField.setText(varName);
1165 
1166             currentTriple = parentGroupGraphPatternPane.addTriple(currentTriple, true);
1167             currentTriple.subjectField.setFieldType(FieldType.Variable);
1168             currentTriple.subjectField.setText(varName);
1169         }
1170 
1171         currentTriple.objectField.setText(objectContent);
1172         currentTriple.objectField.setFieldType(objectFieldType);
1173 
1174         findPathButton.requestFocus();
1175     }
1176 
1177     /**
1178      * Identifikuje typ proměnné nejprve na základě informací z dotazu a později
1179      * podle typu dat, který může dle informací z úložiště obsahovat.
1180      *
1181      * @param variableName Název proměnné.
1182      * @param limit Limit.
1183      * @param offset Offset.
1184      * @param orderBy Order by.
1185      * @param identifyTypes Pokud je <code>false</code>, výsledkem budou data a
1186      * ne třídy.
1187      *
1188      * @return Seznam typů.
1189      */
1190     @SuppressWarnings("SameParameterValue")
1191     private Set<Resource> identifyVariable(final String variableName, final int limit, final int offset, String orderBy,
1192             final boolean identifyTypes) {
1193 
1194         // Nalezení kořenové skupiny
1195         GroupGraphPatternPane topParentPane = parentGroupGraphPatternPane;
1196         GroupGraphPatternPane _topParentPane;
1197         while ((_topParentPane = topParentPane.getParentGroupGraphPatternPane()) != null) {
1198             topParentPane = _topParentPane;
1199         }
1200 
1201         final StringBuilder sb = new StringBuilder();
1202         final Set<String> prefixes = new HashSet<>();
1203         final Set<String> constraints = new HashSet<>();
1204         final Set<String> dependingVariables = new HashSet<>();
1205 
1206         // Extrakce částí dotazu relevantních k proměnné
1207         topParentPane.fillInVariableConstraints(variableName, this, prefixes, constraints, dependingVariables);
1208 
1209         // Sestavení dotazu
1210         prefixes.stream().forEach((prefix) -> {
1211             final String iri = parentQueryFormPane.getQueryPrefixesStorage().getPrefixToIri().get(prefix);
1212             QueryFormPane.appendPrologue(sb, prefix, iri);
1213         });
1214 
1215         sb.append("SELECT DISTINCT "); //$NON-NLS-1$
1216         sb.append(Definitions.VARIABLE_PREFIX);
1217 
1218         final String classOfVariableName = "classOf_" + variableName; //$NON-NLS-1$
1219         final String subClassOfVariableName = "subClassOf_" + variableName; //$NON-NLS-1$
1220 
1221         if (identifyTypes) {
1222             sb.append(classOfVariableName);
1223             sb.append(" WHERE { "); //$NON-NLS-1$
1224             constraints.stream().forEach(sb::append);
1225 
1226             // Část dotazu zjišťující typy dat a eliminující z nich rodičovské třídy
1227             sb.append(Definitions.VARIABLE_PREFIX);
1228             sb.append(variableName);
1229             sb.append(" a "); //$NON-NLS-1$
1230             sb.append(Definitions.VARIABLE_PREFIX);
1231             sb.append(classOfVariableName);
1232             sb.append(" . OPTIONAL { "); //$NON-NLS-1$
1233             sb.append(Definitions.VARIABLE_PREFIX);
1234             sb.append(variableName);
1235             sb.append(" a "); //$NON-NLS-1$
1236             sb.append(Definitions.VARIABLE_PREFIX);
1237             sb.append(subClassOfVariableName);
1238             sb.append(" . "); //$NON-NLS-1$
1239             sb.append(Definitions.VARIABLE_PREFIX);
1240             sb.append(subClassOfVariableName);
1241             sb.append(' ');
1242             sb.append(Definitions.IRI_PREFIX);
1243             sb.append(RDFS.subClassOf.getURI());
1244             sb.append(Definitions.IRI_SUFFIX);
1245             sb.append(' ');
1246             sb.append(Definitions.VARIABLE_PREFIX);
1247             sb.append(classOfVariableName);
1248             sb.append(" . FILTER("); //$NON-NLS-1$
1249             sb.append(Definitions.VARIABLE_PREFIX);
1250             sb.append(subClassOfVariableName);
1251             sb.append(" != "); //$NON-NLS-1$
1252             sb.append(Definitions.VARIABLE_PREFIX);
1253             sb.append(classOfVariableName);
1254             sb.append(") . } FILTER(!bound("); //$NON-NLS-1$
1255             sb.append(Definitions.VARIABLE_PREFIX);
1256             sb.append(subClassOfVariableName);
1257             sb.append(")) ."); //$NON-NLS-1$
1258 
1259             sb.append("} "); //$NON-NLS-1$
1260 
1261             System.out.println(sb.toString());
1262         } else {
1263             sb.append(variableName);
1264             sb.append(" WHERE "); //$NON-NLS-1$
1265             constraints.stream().forEach(sb::append);
1266         }
1267 
1268         if (limit > 0) {
1269             sb.append("LIMIT "); //$NON-NLS-1$
1270             sb.append(limit);
1271             sb.append(' ');
1272         }
1273 
1274         if (offset > 0) {
1275             sb.append("OFFSET "); //$NON-NLS-1$
1276             sb.append(offset);
1277             sb.append(' ');
1278         }
1279 
1280         if (orderBy != null) {
1281             orderBy = orderBy.trim();
1282             if (!orderBy.isEmpty()) {
1283                 sb.append("ORDER BY "); //$NON-NLS-1$
1284                 sb.append(orderBy);
1285             }
1286         }
1287 
1288         final Set<Resource> result = new HashSet<>();
1289         final Query query = DataAgent.createQuery(sb.toString());
1290 
1291         if (identifyTypes) {
1292             // Zjištění typu proměnné z informací v dotazu
1293             result.addAll(DataHelper.extractVariableTypes(query, variableName));
1294         }
1295 
1296         final String resultVariableName = identifyTypes ? classOfVariableName : variableName;
1297 
1298         // Zpracování dotazu
1299         parentQueryFormPane.getDataAgent().select(query, new CancellableConsumer<QuerySolution>() {
1300             @Override
1301             public final void accept(final QuerySolution t) {
1302                 final RDFNode node = t.get(resultVariableName);
1303                 if ((node != null) && (node.isResource())) {
1304                     result.add(node.asResource());
1305                 }
1306             }
1307         });
1308 
1309         return result;
1310     }
1311 
1312     /**
1313      *
1314      */
1315     @Override
1316     public final void fillInVariableConstraints(final String variableName,
1317             final VariableConstraintsGenerator nodeToSkip, final Set<String> prefixes,
1318             final Set<String> constraints, final Set<String> dependingVariables) {
1319         // Hledání omezujících podmínek pro stanovení hodnoty proměnné v dotazu
1320 
1321         final Set<String> variables = getVariables();
1322         if ((this == nodeToSkip) || !variables.contains(variableName)) {
1323             return;
1324         }
1325 
1326         variables.remove(variableName);
1327         dependingVariables.addAll(variables);
1328         prefixes.addAll(getPrefixesUsed(false));
1329         constraints.add(getQueryPart());
1330     }
1331 
1332     /**
1333      *
1334      */
1335     @Override
1336     public final void save(final Element root) {
1337         final Document doc = root.getOwnerDocument();
1338 
1339         if (parentTriplePaneProperty.get() == null) {
1340             final ObservableList<Node> subTriples = container.getChildren();
1341             if (!subTriples.isEmpty()) {
1342                 final Element subTriplesElement = doc.createElement(XML_SUBTRIPLES_ELEMENT_NAME);
1343                 root.appendChild(subTriplesElement);
1344                 Saveable.defaultSave(subTriplesElement, subTriples);
1345             }
1346 
1347             root.appendChild(saveField(doc, XML_SUBJECT_ELEMENT_NAME, subjectField, null, null));
1348         }
1349 
1350         root.appendChild(saveField(doc, XML_PREDICATE_ELEMENT_NAME, propertyField, null, null));
1351         root.appendChild(saveField(doc, XML_OBJECT_ELEMENT_NAME, objectField, langTextField, objectDataTypeComboBox));
1352     }
1353 
1354     /**
1355      * Uloží trojici VŽDY včetně subjektu.
1356      *
1357      * @param root Cílový XML element.
1358      */
1359     private void saveFull(final Element root) {
1360         save(root);
1361         if (parentTriplePaneProperty.get() != null) {
1362             final Document doc = root.getOwnerDocument();
1363             root.appendChild(saveField(doc, XML_SUBJECT_ELEMENT_NAME, subjectField, null, null));
1364         }
1365     }
1366 
1367     /**
1368      *
1369      */
1370     private Element saveField(final Document doc, final String tagName, final TypedTextField field,
1371             final TextField langTextField,
1372             final ComboBox<String> dataTypeComboBox) {
1373         final Element e = doc.createElement(tagName);
1374 
1375         final FieldType fieldType = field.getFieldType();
1376         e.setAttribute(XML_FIELD_TYPE, fieldType.name().toLowerCase());
1377 
1378         switch (fieldType) {
1379             case Literal:
1380                 switch (literalTypeProperty.get()) {
1381                     case LocalizedString:
1382                         e.setAttribute(XML_LITERAL_LANG, langTextField.getText().trim());
1383                         break;
1384                     case Typed:
1385                         e.setAttribute(XML_LITERAL_DATA_TYPE, dataTypeComboBox.getValue().trim());
1386                         break;
1387                     default:
1388                         break;
1389                 }
1390             case IRI:
1391             case Variable:
1392             case PrefixedName:
1393             case BlankNode:
1394             case A:
1395                 e.setAttribute(XML_FIELD_TEXT, field.getText().trim());
1396                 break;
1397             default:
1398                 break;
1399         }
1400         return e;
1401     }
1402 
1403     /**
1404      *
1405      */
1406     @Override
1407     public final void load(final Object e) throws LoadException {
1408 
1409         if (e instanceof Element) {
1410             final NodeList childNodes = ((Element) e).getChildNodes();
1411             final int childNodesCount = childNodes.getLength();
1412             boolean gotSubject = parentTriplePaneProperty.get() != null;
1413             boolean gotPredicate = false;
1414             boolean gotObject = false;
1415             boolean gotSubTriples = false;
1416             for (int index = 0; index < childNodesCount; index++) {
1417                 final org.w3c.dom.Node node = childNodes.item(index);
1418                 if (!(node instanceof Element)) {
1419                     continue;
1420                 }
1421                 final Element childElement = (Element) node;
1422                 final String tagName = childElement.getTagName();
1423                 if (!gotSubject && tagName.equals(XML_SUBJECT_ELEMENT_NAME)) {
1424                     SubjectField.loadSubjectField(this, childElement);
1425                     gotSubject = true;
1426                 } else if (!gotPredicate && tagName.equals(XML_PREDICATE_ELEMENT_NAME)) {
1427                     PropertyField.loadPredicateField(this, childElement);
1428                     gotPredicate = true;
1429                 } else if (!gotObject && tagName.equals(XML_OBJECT_ELEMENT_NAME)) {
1430                     ObjectField.loadObjectField(this, childElement);
1431                     gotObject = true;
1432                 } else if (!gotSubTriples && tagName.equals(XML_SUBTRIPLES_ELEMENT_NAME)) {
1433                     loadSubTriples(childElement);
1434                     gotSubTriples = true;
1435                 }
1436                 if (gotSubject && gotPredicate && gotObject) {
1437                     break;
1438                 }
1439             }
1440 
1441             if (!gotSubject) {
1442                 throw new LoadException(XML_SUBJECT_ELEMENT_NAME);
1443             }
1444             if (!gotPredicate) {
1445                 throw new LoadException(XML_PREDICATE_ELEMENT_NAME);
1446             }
1447             if (!gotObject) {
1448                 throw new LoadException(XML_OBJECT_ELEMENT_NAME);
1449             }
1450         } else if (e instanceof SparqlParserTriple) {
1451             SparqlParserTriple eNode = (SparqlParserTriple) e;
1452             SubjectField.loadSubjectField(this, eNode);
1453             PropertyField.loadPredicateField(this, eNode);
1454             ObjectField.loadObjectField(this, eNode);
1455         }
1456     }
1457 
1458     /**
1459      *
1460      */
1461     static FieldType loadFieldType(final Object e, final FieldType fieldTypeEElement,
1462             final FieldType... allowedFieldTypes) throws LoadException {
1463         FieldType fieldType = null;
1464         if (e instanceof Element) {
1465             Element eNode = (Element) e;
1466             final String fieldTypeString = eNode.getAttribute(XML_FIELD_TYPE);
1467             fieldType = FieldType.valueOfIgnoreCase(fieldTypeString);
1468             if ((fieldType == null) || !fieldType.oneOf(allowedFieldTypes)) {
1469                 throw new LoadException(eNode, XML_FIELD_TYPE);
1470             }
1471         } else if (e instanceof SparqlParserTriple) {
1472             fieldType = fieldTypeEElement;
1473             if ((fieldType == null) || !fieldType.oneOf(allowedFieldTypes)) {
1474                 throw new LoadException(FIELD_TYPE);
1475             }
1476         }
1477         return fieldType;
1478     }
1479 
1480     /**
1481      *
1482      */
1483     static void loadSimpleField(Object e, final TypedTextField field) throws LoadException {
1484         if (e instanceof Element) {
1485             Element eNode = (Element) e;
1486             final FieldType fieldType = loadFieldType(eNode, null, field.getAllowedFieldTypes());
1487             field.setFieldType(fieldType);
1488             if (fieldType != FieldType.A) {
1489                 field.setText(eNode.getAttribute(XML_FIELD_TEXT));
1490             }
1491         } else if (e instanceof SparqlParserTriple) {
1492             SparqlParserTriple eNode = (SparqlParserTriple) e;
1493             FieldProperties fieldProperties = new FieldProperties(eNode, field);
1494             FieldType type = loadFieldType(eNode, fieldProperties.getElemeFieldType(), field.getAllowedFieldTypes());
1495             field.setFieldType(type);
1496             if (type != FieldType.A) {
1497                 field.setText(fieldProperties.getElementValue());
1498             }
1499         }
1500     }
1501 
1502     /**
1503      *
1504      */
1505     private void loadSubTriples(final Element e) throws LoadException {
1506         final NodeList childNodes = e.getElementsByTagName(XML_SUBTRIPLE_ELEMENT_NAME);
1507         final int childNodesCount = childNodes.getLength();
1508         for (int index = 0; index < childNodesCount; index++) {
1509             final org.w3c.dom.Node node = childNodes.item(index);
1510             if (node instanceof Element) {
1511                 addSubTriple(false).load(node);
1512             }
1513         }
1514     }
1515 
1516     /**
1517      *
1518      */
1519     @Override
1520     public final String getXMLElementName() {
1521         return parentTriplePaneProperty.get() == null ? XML_ELEMENT_NAME : XML_SUBTRIPLE_ELEMENT_NAME;
1522     }
1523 
1524     @Override
1525     public final Set<InvalidationListener> getObservers() {
1526         return observers;
1527     }
1528 
1529     /**
1530      *
1531      */
1532     private String toXML()
1533             throws TransformerException, TransformerFactoryConfigurationError,
1534             ParserConfigurationException {
1535         final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
1536         final Element root = doc.createElement(XML_ELEMENT_NAME);
1537         doc.appendChild(root);
1538         saveFull(root);
1539         final StringWriter buffer = new StringWriter();
1540         final StreamResult result = new StreamResult(buffer);
1541         TransformerFactory.newInstance().newTransformer().transform(new DOMSource(doc), result);
1542         return buffer.toString();
1543     }
1544 
1545     /**
1546      * Load triple pane from XML.
1547      *
1548      * @param xml content of source XML file.
1549      * @throws javax.xml.transform.TransformerException Transformation
1550      * exception.
1551      * @throws javax.xml.parsers.ParserConfigurationException Parser exception.
1552      * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException Load exception.
1553      */
1554     public void fromXML(final String xml)
1555             throws TransformerException, TransformerFactoryConfigurationError,
1556             ParserConfigurationException, LoadException {
1557         final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
1558         final StreamSource source = new StreamSource(new StringReader(xml));
1559         TransformerFactory.newInstance().newTransformer().transform(source, new DOMResult(doc));
1560         final NodeList nodes = doc.getElementsByTagName(XML_ELEMENT_NAME);
1561         if (nodes.getLength() > 0) {
1562             load(nodes.item(0));
1563         }
1564     }
1565 
1566     public TypedTextField getSubjectField() {
1567         return subjectField;
1568     }
1569 
1570     public TypedTextField getPropertyField() {
1571         return propertyField;
1572     }
1573 
1574     public TypedTextField getObjectField() {
1575         return objectField;
1576     }
1577 
1578     public QueryFormPane<?> getParentQueryFormPane() {
1579         return parentQueryFormPane;
1580     }
1581 
1582     public VBox getContainer() {
1583         return container;
1584     }
1585 
1586     public ObservableList<Node> getSiblings() {
1587         return siblings;
1588     }
1589 
1590     public TriplePaneShortcuts getShortcuts() {
1591         return shortcuts;
1592     }
1593 
1594     public AutoCompleteListHandler getVariablesAutoCompleteListHandler() {
1595         return variablesAutoCompleteListHandler;
1596     }
1597 
1598     public AutoCompleteListHandler getPropertiesAutoCompleteListHandler() {
1599         return propertiesAutoCompleteListHandler;
1600     }
1601 
1602     public AutoCompleteListHandler getResourcesAutoCompleteListHandler() {
1603         return resourcesAutoCompleteListHandler;
1604     }
1605 
1606     public HBox getSubjectFieldContainer() {
1607         return subjectFieldContainer;
1608     }
1609 
1610     public HBox getPropertyFieldContainer() {
1611         return propertyFieldContainer;
1612     }
1613 
1614     public HBox getObjectFieldContainer() {
1615         return objectFieldContainer;
1616     }
1617 
1618     public ComboBox<String> getObjectDataTypeComboBox() {
1619         return objectDataTypeComboBox;
1620     }
1621 
1622     public ObjectProperty<LiteralType> getLiteralTypeProperty() {
1623         return literalTypeProperty;
1624     }
1625 
1626     public TextField getLangTextField() {
1627         return langTextField;
1628     }
1629 
1630     public MenuButton getLiteralTypeMenuButton() {
1631         return literalTypeMenuButton;
1632     }
1633 
1634     public ReferenceKeeper getObjectFieldTypePropertyRefKeeper() {
1635         return objectFieldTypePropertyRefKeeper;
1636     }
1637     /*
1638     private final void onDataTypesStorageChanged(final StorageEntry term, final boolean added)
1639 	{
1640 		if (added)
1641 		{
1642 			final ObservableList<String> items = objectDataTypeComboBox.getItems();
1643 			if ((items == null) || items.isEmpty())
1644 			{
1645 				updateDataTypeComboBox();
1646 			}
1647 			return;
1648 		}
1649 
1650 		if ((objectField.getFieldType() != FieldType.Literal) || (literalTypeProperty.get() != LiteralType.Typed))
1651 		{
1652 			return;
1653 		}
1654 
1655 		final String item = objectDataTypeComboBox.getSelectionModel().getSelectedItem();
1656 
1657 		String removedDataTypeIdentifier = term.getShortVariant(parentQueryFormPane.getQueryPrefixesStorage());
1658 		if (removedDataTypeIdentifier == null)
1659 		{
1660 			removedDataTypeIdentifier = term.getWrappedIRI();
1661 		}
1662 
1663 		if (!item.equals(removedDataTypeIdentifier))
1664 		{
1665 			return;
1666 		}
1667 
1668 		if (ConfirmationDialog.open(getScene().getWindow(),
1669 				Messages.getString("WARNING"), String.format(Messages.getString("REMOVED_DATA_TYPE_IN_USE_PUT_IT_BACK"), removedDataTypeIdentifier))) //$NON-NLS-1$ //$NON-NLS-2$
1670 		{
1671 			parentQueryFormPane.getQueryDataTypesStorage().addTerm(term);
1672 			return;
1673 		}
1674 
1675 		updateDataTypeComboBox();
1676 	}*/
1677 
1678 }