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.autoComplete.AutoCompleteListHandler;
24 import cz.zcu.mre.sparkle.gui.query.autoComplete.AutoCompletePolicy;
25 import cz.zcu.mre.sparkle.gui.query.autoComplete.AutoCompleteWrapper;
26 import cz.zcu.mre.sparkle.gui.query.helpers.*;
27 import cz.zcu.mre.sparkle.gui.tools.Components;
28 import cz.zcu.mre.sparkle.tools.Changeable;
29 import cz.zcu.mre.sparkle.tools.Definitions;
30 import cz.zcu.mre.sparkle.tools.Utils;
31 import cz.zcu.mre.sparkle.tools.sparqlValidation.SparqlValidationUtils;
32 import javafx.beans.InvalidationListener;
33 import javafx.beans.property.*;
34 import javafx.fxml.FXML;
35 import javafx.scene.control.Label;
36 import javafx.scene.control.TextField;
37 import javafx.scene.layout.HBox;
38 import javafx.scene.layout.Priority;
39 import java.util.*;
40
41
42
43
44
45
46
47
48
49
50 public final class TypedTextField
51 extends HBox
52 implements VariablesGenerator, PrefixesUser, Changeable {
53
54 @FXML
55 private Label prefixLabel;
56 @FXML
57 private Label suffixLabel;
58 @FXML
59 private TextField textField;
60
61 private final Map<FieldType, AutoCompleteListHandler> autoCompleteHandlers;
62 private final AutoCompleteWrapper autoComplete;
63 private final ReadOnlyStringWrapper valueProperty = new ReadOnlyStringWrapper();
64 private final FieldTypeWrapper fieldTypeWrapper;
65 private final ObjectProperty<TypedTextField> boundTextFieldProperty = new SimpleObjectProperty<>(null);
66 private final VariableFieldListener variableListener = new VariableFieldListener(this);
67 private final Set<VariablesCollector> variablesCollectors = new HashSet<>();
68 private final Set<InvalidationListener> observers = new HashSet<>();
69
70 public TypedTextField(final PrefixesStorage prefixesStorage, boolean checkValues, final FieldType initialType,
71 final FieldType... otherOptions) {
72
73 Components.load(this);
74
75 fieldTypeWrapper = new FieldTypeWrapper(textField, prefixesStorage, initialType, checkValues, otherOptions);
76
77 autoComplete = new AutoCompleteWrapper(textField, null);
78 autoCompleteHandlers = new HashMap<>();
79
80
81 init();
82 }
83
84 public TypedTextField(final PrefixesStorage prefixesStorage, final TypedTextField boundTextField) {
85 Components.load(this);
86
87 fieldTypeWrapper = boundTextField.fieldTypeWrapper;
88 boundTextFieldProperty.set(boundTextField);
89 autoComplete = null;
90 autoCompleteHandlers = null;
91
92 init();
93 }
94
95
96
97
98 private void init() {
99 setupListeners();
100 setupBinding();
101 onFieldTypeChanged(null, fieldTypeWrapper.getFieldType());
102
103 HBox.setHgrow(this, Priority.ALWAYS);
104 }
105
106
107
108
109 private void setupListeners() {
110 fieldTypeWrapper.fieldTypeProperty()
111 .addListener((observable, oldValue, newValue) -> onFieldTypeChanged(oldValue, newValue));
112 textField.textProperty().addListener((o) -> updateValue());
113 updateValue();
114 }
115
116
117
118
119 private void setupBinding() {
120 Utils.makeInvisibleUnmanaged(prefixLabel, suffixLabel);
121
122 final ObjectProperty<FieldType> ft = fieldTypeWrapper.fieldTypeProperty();
123 prefixLabel.visibleProperty().bind(
124 ft.isEqualTo(FieldType.Expression).or(ft.isEqualTo(FieldType.IRI)).or(ft.isEqualTo(FieldType.Variable))
125 .or(ft.isEqualTo(FieldType.Literal)));
126 suffixLabel.visibleProperty().bind(ft.isEqualTo(FieldType.Expression).or(ft.isEqualTo(FieldType.IRI))
127 .or(ft.isEqualTo(FieldType.Literal)));
128 textField.editableProperty().bind(
129 ft.isEqualTo(FieldType.Expression).or(ft.isEqualTo(FieldType.IRI)).or(ft.isEqualTo(FieldType.Variable))
130 .or(ft.isEqualTo(FieldType.Literal))
131 .or(ft.isEqualTo(FieldType.PrefixedName)).or(ft.isEqualTo(FieldType.Modifier))
132 .or(ft.isEqualTo(FieldType.BlankNode)).or(ft.isEqualTo(FieldType.Nil))
133 .or(ft.isEqualTo(FieldType.Numeric)).or(ft.isEqualTo(FieldType.Boolean)));
134
135 textField.disableProperty().bind(boundTextFieldProperty.isNotNull());
136
137 if (autoComplete != null) {
138 autoComplete.autoPopupOffsetProperty().bind(ft.isEqualTo(FieldType.Expression));
139 autoComplete.openOnFocusProperty()
140 .bind(ft.isEqualTo(FieldType.Variable).or(ft.isEqualTo(FieldType.PrefixedName)));
141 }
142
143 final TypedTextField boundTextField = boundTextFieldProperty.get();
144 if (boundTextField != null) {
145 textField.textProperty().bindBidirectional(boundTextField.textField.textProperty());
146 textField.promptTextProperty().bindBidirectional(boundTextField.textField.promptTextProperty());
147 }
148 }
149
150
151
152
153
154 private void onFieldTypeChanged(final FieldType oldType, final FieldType newType) {
155 updateLabels(newType);
156 updateAutoComplete(newType);
157 updateListeners(oldType, newType);
158 updateContent(newType);
159 updateValue();
160 }
161
162
163
164
165
166 private void updateListeners(final FieldType oldType, final FieldType newType) {
167 if (newType == FieldType.Variable) {
168 textField.textProperty().addListener(variableListener);
169 final String variableName = textField.getText().trim();
170 if (SparqlValidationUtils.isVariableNameValid(variableName)) {
171 notifyVariableAdded(variableName);
172 }
173 } else if (oldType == FieldType.Variable) {
174 textField.textProperty().removeListener(variableListener);
175 final String variableName = textField.getText().trim();
176 if (SparqlValidationUtils.isVariableNameValid(variableName)) {
177 notifyVariableRemoved(variableName);
178 }
179 }
180 }
181
182
183
184
185 private void updateContent(final FieldType newType) {
186 if (newType == FieldType.A) {
187 setText(Definitions.RDF_TYPE_SHORTCUT);
188 } else if (newType == FieldType.Undef) {
189 setText(Definitions.RDF_UNDEF);
190 }
191 textField.end();
192 }
193
194
195
196
197 private void updateAutoComplete(final FieldType type) {
198 if (autoComplete == null) {
199 return;
200 }
201
202 updateAutoCompleteHandler(type);
203 updateAutoCompleteProperties(type);
204 }
205
206
207
208
209
210
211 private void updateAutoCompleteProperties(final FieldType type) {
212 switch (type) {
213 case Expression:
214 autoComplete.setPolicy(AutoCompletePolicy.FinishWord);
215 autoComplete.setOpenOnChars('?', ':');
216 break;
217 case PrefixedName:
218 autoComplete.setPolicy(AutoCompletePolicy.ReplaceAll);
219 autoComplete.setOpenOnChars(':');
220 break;
221 default:
222 autoComplete.setPolicy(AutoCompletePolicy.ReplaceAll);
223 autoComplete.clearOpenOnChars();
224 break;
225 }
226
227 }
228
229
230
231
232 private void updateAutoCompleteHandler(final FieldType type) {
233 final AutoCompleteListHandler handler = autoCompleteHandlers.get(type);
234 autoComplete.setListHandler(handler);
235
236 if (autoComplete.isPopupVisible()) {
237 if (!(textField.isFocused() && autoComplete.getOpenOnFocus())) {
238 autoComplete.hidePopup();
239 }
240 } else if ((handler != null) && textField.isFocused() && autoComplete.getOpenOnFocus()) {
241 autoComplete.showPopup();
242 }
243 }
244
245
246
247
248 private void updateLabels(final FieldType type) {
249 switch (type) {
250 case Expression:
251 prefixLabel.setText(Definitions.EXPRESSION_PREFIX);
252 suffixLabel.setText(Definitions.EXPRESSION_SUFFIX);
253 break;
254 case IRI:
255 prefixLabel.setText(Definitions.IRI_PREFIX);
256 suffixLabel.setText(Definitions.IRI_SUFFIX);
257 break;
258 case Literal:
259 prefixLabel.setText(Definitions.LITERAL_PREFIX);
260 suffixLabel.setText(Definitions.LITERAL_SUFFIX);
261 break;
262 case Variable:
263 prefixLabel.setText(Definitions.VARIABLE_PREFIX);
264 suffixLabel.setText("");
265 break;
266 default:
267 prefixLabel.setText("");
268 suffixLabel.setText("");
269 break;
270
271 }
272 }
273
274
275
276
277 public final ReadOnlyStringProperty valueProperty() {
278 return valueProperty.getReadOnlyProperty();
279 }
280
281
282
283
284
285 public final String getValue() {
286 return valueProperty.get();
287 }
288
289 private void updateValue() {
290 valueProperty.set(prefixLabel.getText()
291 + (getFieldType() == FieldType.Literal ? escapeQuotes(textField.getText().trim())
292 : textField.getText().trim()) + suffixLabel.getText());
293 notifyChanged();
294 }
295
296
297
298
299 public final String getText() {
300 return textField.getText();
301 }
302
303
304
305
306
307
308 public final void setText(final String text) {
309 textField.setText(text);
310 fieldTypeWrapper.setCheckValue(true);
311 }
312
313 public final AutoCompleteListHandler getAutoCompleteListHandler(final FieldType fieldType) {
314 return autoCompleteHandlers.get(fieldType);
315 }
316
317 public final void setAutoCompleteListHandler(final FieldType fieldType, final AutoCompleteListHandler handler) {
318 if (fieldTypeWrapper.getFieldType() == fieldType) {
319 autoComplete.setListHandler(handler);
320 }
321
322 if (handler == null) {
323 autoCompleteHandlers.remove(fieldType);
324 return;
325 }
326
327 autoCompleteHandlers.put(fieldType, handler);
328 }
329
330 public TextField getTextField() {
331 return this.textField;
332 }
333
334 @Override
335 public final Set<String> getVariables() {
336 final Set<String> result;
337 if ((fieldTypeWrapper.getFieldType() == FieldType.Variable)
338 && (fieldTypeWrapper.fieldContentIsValidProperty().get())) {
339 result = new HashSet<>(1);
340 result.add(getText());
341 } else if (fieldTypeWrapper.getFieldType() == FieldType.Expression) {
342 result = Utils.extractMultipleVariables(getText());
343 } else {
344 result = Collections.emptySet();
345 }
346 return result;
347 }
348
349 @Override
350 public final void notifyVariableRemoved(final String variableName) {
351 variablesCollectors.forEach((c) -> c.onVariableRemoved(variableName));
352 }
353
354 @Override
355 public final void notifyVariableAdded(final String variableName) {
356 variablesCollectors.forEach((c) -> c.onVariableAdded(variableName));
357 }
358
359 @Override
360 public final void addVariablesCollector(final VariablesCollector collector) {
361 variablesCollectors.add(collector);
362 }
363
364 @Override
365 public final void removeVariablesCollector(final VariablesCollector collector) {
366 variablesCollectors.remove(collector);
367 }
368
369 @Override
370 public final void notifyVariableChanged(final String oldVariableName, final String newVariableName) {
371 variablesCollectors.forEach((c) -> c.onVariableChanged(oldVariableName, newVariableName));
372 }
373
374 @Override
375 public final Set<String> getPrefixesUsed(final boolean appendDelimiter) {
376 Set<String> result = Collections.emptySet();
377 if (null != fieldTypeWrapper.getFieldType()) {
378 switch (fieldTypeWrapper.getFieldType()) {
379 case PrefixedName:
380 case Expression:
381 result = Utils.extractMultiplePrefixes(getText(), appendDelimiter);
382 break;
383 default:
384 result = Collections.emptySet();
385 break;
386 }
387 }
388 return result;
389 }
390
391 public final String getPlaceholder() {
392 return textField.getPromptText();
393 }
394
395 public final void setPlaceholder(final String text) {
396 textField.setPromptText(text);
397 }
398
399 public final ObjectProperty<FieldType> fieldTypeProperty() {
400 return fieldTypeWrapper.fieldTypeProperty();
401 }
402
403 public final FieldType getFieldType() {
404 return fieldTypeWrapper.getFieldType();
405 }
406
407 public final void setFieldType(final FieldType type) {
408 fieldTypeWrapper.setFieldType(type);
409 }
410
411 public final ReadOnlyBooleanProperty fieldContentIsValidProperty() {
412 return fieldTypeWrapper.fieldContentIsValidProperty();
413 }
414
415 public final ReadOnlyBooleanProperty textFieldFocusedProperty() {
416 return textField.focusedProperty();
417 }
418
419 @Override
420 public final void requestFocus() {
421 textField.requestFocus();
422 }
423
424 public final FieldType[] getAllowedFieldTypes() {
425 return fieldTypeWrapper.getAllowedFieldTypes();
426 }
427
428 public final boolean isAutoCompleteVisible() {
429 return autoComplete != null && autoComplete.isPopupVisible();
430 }
431
432 public final boolean isEmpty() {
433 return textField.getText().trim().isEmpty();
434 }
435
436 @Override
437 public final Set<InvalidationListener> getObservers() {
438 return observers;
439 }
440
441 private static String escapeQuotes(final String unescapedString) {
442 return unescapedString.replace("\"", "\\\"");
443 }
444 }