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.query.other;
21
22 import cz.zcu.mre.sparkle.data.PrefixesStorage;
23 import cz.zcu.mre.sparkle.gui.query.helpers.FieldType;
24 import cz.zcu.mre.sparkle.gui.query.helpers.RegionPopup;
25 import cz.zcu.mre.sparkle.gui.query.helpers.RegionPopup.PopupAlignment;
26 import cz.zcu.mre.sparkle.gui.query.helpers.RegionPopup.PopupPlacement;
27 import cz.zcu.mre.sparkle.gui.tools.Components;
28 import cz.zcu.mre.sparkle.gui.tools.ReferenceKeeper;
29 import cz.zcu.mre.sparkle.tools.Utils;
30 import cz.zcu.mre.sparkle.tools.sparqlValidation.SparqlValidationUtils;
31 import javafx.beans.Observable;
32 import javafx.beans.property.*;
33 import javafx.collections.ObservableList;
34 import javafx.event.Event;
35 import javafx.fxml.FXML;
36 import javafx.scene.Node;
37 import javafx.scene.control.TextField;
38 import javafx.scene.control.Toggle;
39 import javafx.scene.control.ToggleButton;
40 import javafx.scene.control.ToggleGroup;
41 import javafx.scene.input.KeyEvent;
42 import javafx.scene.layout.HBox;
43 import org.apache.jena.sparql.util.ExprUtils;
44 import java.util.*;
45
46
47
48
49
50
51
52
53
54 final class FieldTypeWrapper
55 extends HBox {
56
57 @FXML
58 private ToggleButton variableToggleButton, prefixedNameToggleButton, iriToggleButton, aToggleButton,
59 literalToggleButton, expressionToggleButton, modifierToggleButton, blankNodeToggleButton, nilToggleButton,
60 undefToggleButton, booleanToggleButton, numericToggleButton;
61 @FXML
62 private ToggleGroup fieldTypeToggleGroup;
63
64 private TextField textField;
65 private final Set<FieldType> allowedOptions = new HashSet<>();
66 private final RegionPopup popup;
67 private final ObjectProperty<FieldType> fieldTypeProperty = new SimpleObjectProperty<>();
68 private final BooleanProperty fieldContentIsValidProperty = new SimpleBooleanProperty(true);
69 private final ReadOnlyBooleanProperty fieldContentIsValidReadOnlyProperty = ReadOnlyBooleanProperty.readOnlyBooleanProperty(fieldContentIsValidProperty);
70 private final PrefixesStorage prefixesStorage;
71 private final ReferenceKeeper keeper = new ReferenceKeeper();
72 private boolean checkValue;
73
74
75 public FieldTypeWrapper(final TextField textField, final PrefixesStorage prefixesStorage,
76 final FieldType initialType, boolean checkValue, final FieldType... otherOptions) {
77 Components.load(this);
78
79 this.textField = textField;
80 this.prefixesStorage = prefixesStorage;
81 this.checkValue = checkValue;
82
83
84 for (final FieldType fieldType : FieldType.values()) {
85 getToggle(fieldType).setUserData(fieldType);
86 }
87
88
89 fieldTypeProperty.addListener(keeper.toWeak((observable, oldValue, newValue) -> {
90 getToggle(newValue).setSelected(true);
91 updateFieldContentIsValid();
92 }));
93
94
95 allowedOptions.add(initialType);
96 Collections.addAll(allowedOptions, otherOptions);
97
98
99 final List<FieldType> allOptionsList = new LinkedList<>(Arrays.asList(FieldType.values()));
100 allOptionsList.removeAll(allowedOptions);
101 allOptionsList.stream().map(this::getToggle).filter((toggle) -> (toggle instanceof Node))
102 .peek((toggle) -> ((Node) toggle).setVisible(false)).forEach((toggle) -> ((Node) toggle).setManaged(false));
103
104
105 popup = new RegionPopup(textField, PopupPlacement.TOP, PopupAlignment.LEFT_OR_TOP);
106 popup.getContent().add(this);
107 popup.setAutoFix(false);
108 popup.setConsumeAutoHidingEvents(false);
109 popup.setAutoHide(true);
110 popup.setHideOnEscape(false);
111
112
113 textField.focusedProperty().addListener(keeper.toWeak((observable, oldValue, newValue) -> {
114 if (newValue && !popup.isVisible()) {
115 popup.showPositioned();
116 } else if (popup.isVisible()) {
117 popup.hide();
118 }
119 }));
120
121
122 textField.setOnMouseClicked(keeper.toWeak((final Event event) -> {
123 if (textField.isFocused() && !popup.isVisible()) {
124 popup.showPositioned();
125 }
126 }));
127
128
129 fieldTypeToggleGroup.selectedToggleProperty().addListener(keeper.toWeak((observable, oldValue, newValue) -> {
130 if (newValue == null) {
131 if (oldValue != null) {
132 setFieldType((FieldType) oldValue.getUserData());
133 }
134 } else if (!fieldTypeProperty.isBound()) {
135 fieldTypeProperty.set((FieldType) newValue.getUserData());
136 }
137 }));
138
139
140 textField.addEventFilter(KeyEvent.KEY_PRESSED, keeper.toWeak((final KeyEvent event) -> {
141 if (event.isAltDown()) {
142 switch (event.getCode()) {
143 case LEFT:
144 selectPrevious();
145 event.consume();
146 break;
147 case RIGHT:
148 selectNext();
149 event.consume();
150 break;
151 default:
152 }
153 }
154 }));
155
156
157 textField.textProperty()
158 .addListener(keeper.toWeak((final Observable observable) -> updateFieldContentIsValid()));
159
160
161 fieldContentIsValidProperty.addListener(keeper.toWeak((observable, oldValue, newValue) -> {
162 if (newValue) {
163 textField.getStyleClass().remove("invalid-field");
164 } else {
165 textField.getStyleClass().add("invalid-field");
166 }
167 }));
168
169 setFieldType(initialType);
170 }
171
172
173
174
175
176 private void updateFieldContentIsValid() {
177
178 switch (fieldTypeProperty.get()) {
179 case A:
180 fieldContentIsValidProperty.set(true);
181 break;
182 case Expression:
183 try {
184 ExprUtils.parse(textField.getText(), prefixesStorage);
185 fieldContentIsValidProperty.set(true);
186 } catch (final Exception e) {
187 fieldContentIsValidProperty.set(false);
188 }
189 break;
190 case IRI:
191 fieldContentIsValidProperty.set(SparqlValidationUtils.isIRIValid(textField.getText(), false));
192 break;
193 case Literal:
194 fieldContentIsValidProperty.set(true);
195 break;
196 case PrefixedName:
197 fieldContentIsValidProperty.set(SparqlValidationUtils.isPrefixedNameValid(textField.getText(), this.checkValue));
198 String prefix = Utils.extractPrefix(textField.getText());
199 fieldContentIsValidProperty.set(prefixesStorage.getPrefixToIri().containsKey(prefix));
200 break;
201 case Variable:
202 fieldContentIsValidProperty.set(SparqlValidationUtils.isVariableNameValid(textField.getText()));
203 break;
204 case BlankNode:
205 fieldContentIsValidProperty.set(Utils.isFieldNotEmpty(textField.getText()));
206 break;
207 case Nil:
208 fieldContentIsValidProperty.set(SparqlValidationUtils.isNilValid(textField.getText()));
209 break;
210 case Undef:
211 fieldContentIsValidProperty.set(true);
212 break;
213 case Boolean:
214 fieldContentIsValidProperty.set(SparqlValidationUtils.isBooleanValid(textField.getText()));
215 break;
216 case Numeric:
217 fieldContentIsValidProperty.set(SparqlValidationUtils.isNumericValid(textField.getText()));
218 break;
219 default:
220 fieldContentIsValidProperty.set(true);
221 break;
222 }
223 }
224
225 public void setCheckValue(boolean state) {
226 this.checkValue = state;
227 }
228
229
230
231
232
233
234
235
236 private Toggle getToggle(final FieldType fieldType) {
237 switch (fieldType) {
238 case A:
239 return aToggleButton;
240 case IRI:
241 return iriToggleButton;
242 case Literal:
243 return literalToggleButton;
244 case PrefixedName:
245 return prefixedNameToggleButton;
246 case Variable:
247 return variableToggleButton;
248 case Modifier:
249 return modifierToggleButton;
250 case BlankNode:
251 return blankNodeToggleButton;
252 case Nil:
253 return nilToggleButton;
254 case Undef:
255 return undefToggleButton;
256 case Boolean:
257 return booleanToggleButton;
258 case Numeric:
259 return numericToggleButton;
260 case Expression:
261 return expressionToggleButton;
262 default:
263 return null;
264 }
265 }
266
267 public final FieldType getFieldType() {
268 return fieldTypeProperty.get();
269 }
270
271 public final void setFieldType(final FieldType fieldType) {
272 if (allowedOptions.contains(fieldType)) {
273 fieldTypeToggleGroup.selectToggle(getToggle(fieldType));
274 } else {
275 throw new IllegalArgumentException(
276 "Field type " + fieldType + " is not allowed here.");
277 }
278 }
279
280 public final ObjectProperty<FieldType> fieldTypeProperty() {
281 return fieldTypeProperty;
282 }
283
284 public final TextField getTextField() {
285 return textField;
286 }
287
288 public final void setTextField(final TextField textField) {
289 if (this.textField != textField) {
290 if (popup.isVisible()) {
291 popup.hide();
292 }
293
294 this.textField = textField;
295
296 popup.showPositioned();
297 }
298 }
299
300 private ObservableList<Node> getVisibleToggles() {
301 return getChildren().filtered((t) -> {
302 final Object o = t.getUserData();
303 return (t.isVisible()) && (t instanceof Toggle) && (o instanceof FieldType);
304 });
305 }
306
307
308
309
310
311
312
313
314 private void selectPreviousOrNext(final boolean next) {
315 final ObservableList<Node> visibleToggles = getVisibleToggles();
316 if (visibleToggles.isEmpty() || (visibleToggles.size() == 1)) {
317 return;
318 }
319 final Toggle currentToggle = getToggle(fieldTypeProperty.get());
320 int currentToggleIndex = visibleToggles.indexOf(currentToggle);
321
322 if (currentToggleIndex == -1) {
323 setFieldType((FieldType) visibleToggles.get(0).getUserData());
324 return;
325 }
326
327 if (next) {
328 currentToggleIndex++;
329 currentToggleIndex %= visibleToggles.size();
330 } else {
331 currentToggleIndex = (currentToggleIndex == 0) ? visibleToggles.size() - 1 : currentToggleIndex - 1;
332 }
333
334 setFieldType((FieldType) visibleToggles.get(currentToggleIndex).getUserData());
335 }
336
337
338
339
340 private void selectNext() {
341 selectPreviousOrNext(true);
342 }
343
344
345
346
347 private void selectPrevious() {
348 selectPreviousOrNext(false);
349 }
350
351
352
353
354 public final ReadOnlyBooleanProperty fieldContentIsValidProperty() {
355 return fieldContentIsValidReadOnlyProperty;
356 }
357
358
359
360
361 public final FieldType[] getAllowedFieldTypes() {
362 return allowedOptions.toArray(new FieldType[0]);
363 }
364 }