001 /*
002 * Copyright 2008-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package griffon.javafx.support;
017
018 import griffon.core.artifact.GriffonController;
019 import griffon.core.controller.Action;
020 import griffon.core.controller.ActionManager;
021 import griffon.core.editors.ValueConversionException;
022 import griffon.exceptions.InstanceMethodInvocationException;
023 import javafx.application.Platform;
024 import javafx.beans.property.Property;
025 import javafx.collections.ListChangeListener;
026 import javafx.collections.ObservableList;
027 import javafx.event.ActionEvent;
028 import javafx.scene.Node;
029 import javafx.scene.Parent;
030 import javafx.scene.Scene;
031 import javafx.scene.control.Accordion;
032 import javafx.scene.control.ButtonBase;
033 import javafx.scene.control.ButtonBar;
034 import javafx.scene.control.CheckBox;
035 import javafx.scene.control.CheckMenuItem;
036 import javafx.scene.control.ContextMenu;
037 import javafx.scene.control.Control;
038 import javafx.scene.control.Labeled;
039 import javafx.scene.control.Menu;
040 import javafx.scene.control.MenuBar;
041 import javafx.scene.control.MenuItem;
042 import javafx.scene.control.RadioButton;
043 import javafx.scene.control.RadioMenuItem;
044 import javafx.scene.control.ScrollPane;
045 import javafx.scene.control.SplitPane;
046 import javafx.scene.control.Tab;
047 import javafx.scene.control.TabPane;
048 import javafx.scene.control.TitledPane;
049 import javafx.scene.control.ToggleButton;
050 import javafx.scene.control.ToolBar;
051 import javafx.scene.control.Tooltip;
052 import javafx.scene.image.Image;
053 import javafx.scene.image.ImageView;
054 import javafx.stage.Window;
055
056 import javax.annotation.Nonnull;
057 import javax.annotation.Nullable;
058 import java.lang.reflect.Constructor;
059 import java.lang.reflect.InvocationTargetException;
060 import java.net.URL;
061 import java.util.LinkedHashSet;
062 import java.util.Map;
063 import java.util.Set;
064
065 import static griffon.util.GriffonClassUtils.getGetterName;
066 import static griffon.util.GriffonClassUtils.getPropertyValue;
067 import static griffon.util.GriffonClassUtils.invokeExactInstanceMethod;
068 import static griffon.util.GriffonClassUtils.invokeInstanceMethod;
069 import static griffon.util.GriffonNameUtils.isBlank;
070 import static griffon.util.GriffonNameUtils.requireNonBlank;
071 import static java.util.Objects.requireNonNull;
072
073 /**
074 * @author Andres Almiray
075 */
076 public final class JavaFXUtils {
077 private static final String ERROR_NODE_NULL = "Argument 'node' must not be null";
078 private static final String ERROR_CONTROL_NULL = "Argument 'control' must not be null";
079 private static final String ERROR_ACTION_NULL = "Argument 'action' must not be null";
080 private static final String ERROR_ICON_BLANK = "Argument 'iconUrl' must not be blank";
081 private static final String ERROR_ID_BLANK = "Argument 'id' must not be blank";
082 private static final String ERROR_URL_BLANK = "Argument 'url' must not be blank";
083 private static final String ERROR_ROOT_NULL = "Argument 'root' must not be null";
084 private static final String ERROR_CONTROLLER_NULL = "Argument 'controller' must not be null";
085 private static final String ACTION_TARGET_SUFFIX = "ActionTarget";
086 private static final String PROPERTY_SUFFIX = "Property";
087
088 private JavaFXUtils() {
089
090 }
091
092 /**
093 * Wraps an <tt>ObservableList</tt>, publishing updates inside the UI thread.
094 *
095 * @param source the <tt>ObservableList</tt> to be wrapped
096 * @param <E> the list's paramter type.
097 * @return a new <tt>ObservableList</tt>
098 * @since 2.6.0
099 */
100 @Nonnull
101 public static <E> ObservableList<E> createJavaFXThreadProxyList(@Nonnull ObservableList<E> source) {
102 requireNonNull(source, "Argument 'source' must not be null");
103 return new JavaFXThreadProxyObservableList<>(source);
104 }
105
106 private static class JavaFXThreadProxyObservableList<E> extends DelegatingObservableList<E> {
107 protected JavaFXThreadProxyObservableList(ObservableList<E> delegate) {
108 super(delegate);
109 }
110
111 @Override
112 protected void sourceChanged(@Nonnull final ListChangeListener.Change<? extends E> c) {
113 if (Platform.isFxApplicationThread()) {
114 fireChange(c);
115 } else {
116 Platform.runLater(() -> fireChange(c));
117 }
118 }
119 }
120
121 @Nonnull
122 @SuppressWarnings("ConstantConditions")
123 public static <B> Property<?> extractProperty(@Nonnull B bean, @Nonnull String propertyName) {
124 requireNonNull(bean, "Argument 'bean' must not be null");
125 requireNonBlank(propertyName, "Argument 'propertyName' must not be null");
126
127 if (!propertyName.endsWith(PROPERTY_SUFFIX)) {
128 propertyName += PROPERTY_SUFFIX;
129 }
130
131 InstanceMethodInvocationException imie;
132 try {
133 // 1. try <columnName>Property() first
134 return (Property<?>) invokeExactInstanceMethod(bean, propertyName);
135 } catch (InstanceMethodInvocationException e) {
136 imie = e;
137 }
138
139 // 2. fallback to get<columnName>Property()
140 try {
141 return (Property<?>) invokeExactInstanceMethod(bean, getGetterName(propertyName));
142 } catch (InstanceMethodInvocationException e) {
143 throw imie;
144 }
145 }
146
147 public static void connectActions(@Nonnull Object node, @Nonnull GriffonController controller) {
148 requireNonNull(node, ERROR_NODE_NULL);
149 requireNonNull(controller, ERROR_CONTROLLER_NULL);
150 ActionManager actionManager = controller.getApplication().getActionManager();
151 for (Map.Entry<String, Action> e : actionManager.actionsFor(controller).entrySet()) {
152 String actionTargetName = actionManager.normalizeName(e.getKey()) + ACTION_TARGET_SUFFIX;
153 Object control = findElement(node, actionTargetName);
154 if (control == null) continue;
155 JavaFXAction action = (JavaFXAction) e.getValue().getToolkitAction();
156
157 if (control instanceof ButtonBase) {
158 configure(((ButtonBase) control), action);
159 } else if (control instanceof MenuItem) {
160 JavaFXUtils.configure(((MenuItem) control), action);
161 } else if (control instanceof Node) {
162 ((Node) control).addEventHandler(ActionEvent.ACTION, action.getOnAction());
163 } else {
164 // does it support the onAction property?
165 try {
166 invokeInstanceMethod(control, "setOnAction", action.getOnAction());
167 } catch (InstanceMethodInvocationException imie) {
168 // ignore
169 }
170 }
171 }
172 }
173
174 private static void runInsideUIThread(@Nonnull Runnable runnable) {
175 if (Platform.isFxApplicationThread()) {
176 runnable.run();
177 } else {
178 Platform.runLater(runnable);
179 }
180 }
181
182 public static void configure(final @Nonnull ToggleButton control, final @Nonnull JavaFXAction action) {
183 configure((ButtonBase) control, action);
184
185 action.selectedProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setSelected(n)));
186 runInsideUIThread(() -> control.setSelected(action.isSelected()));
187 }
188
189 public static void configure(final @Nonnull CheckBox control, final @Nonnull JavaFXAction action) {
190 configure((ButtonBase) control, action);
191
192 action.selectedProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setSelected(n)));
193 runInsideUIThread(() -> control.setSelected(action.isSelected()));
194 }
195
196 public static void configure(final @Nonnull RadioButton control, final @Nonnull JavaFXAction action) {
197 configure((ButtonBase) control, action);
198
199 action.selectedProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setSelected(n)));
200 runInsideUIThread(() -> control.setSelected(action.isSelected()));
201 }
202
203 public static void configure(final @Nonnull ButtonBase control, final @Nonnull JavaFXAction action) {
204 requireNonNull(control, ERROR_CONTROL_NULL);
205 requireNonNull(action, ERROR_ACTION_NULL);
206
207 action.onActionProperty().addListener((v, o, n) -> control.setOnAction(n));
208 control.setOnAction(action.getOnAction());
209
210 action.nameProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setText(n)));
211 runInsideUIThread(() -> control.setText(action.getName()));
212
213 action.descriptionProperty().addListener((v, o, n) -> setTooltip(control, n));
214 setTooltip(control, action.getDescription());
215
216 action.iconProperty().addListener((v, o, n) -> setIcon(control, n));
217 if (!isBlank(action.getIcon())) {
218 setIcon(control, action.getIcon());
219 }
220
221 action.imageProperty().addListener((v, o, n) -> setGraphic(control, n));
222 if (null != action.getImage()) {
223 setGraphic(control, action.getImage());
224 }
225
226 action.graphicProperty().addListener((v, o, n) -> setGraphic(control, n));
227 if (null != action.getGraphic()) {
228 setGraphic(control, action.getGraphic());
229 }
230
231 action.enabledProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setDisable(!n)));
232 runInsideUIThread(() -> control.setDisable(!action.isEnabled()));
233
234 action.visibleProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setVisible(n)));
235 runInsideUIThread(() -> control.setVisible(action.isVisible()));
236
237 action.styleClassProperty().addListener((v, o, n) -> {
238 setStyleClass(control, o, true);
239 setStyleClass(control, n);
240 });
241 setStyleClass(control, action.getStyleClass());
242 }
243
244 public static void configure(final @Nonnull CheckMenuItem control, final @Nonnull JavaFXAction action) {
245 configure((MenuItem) control, action);
246
247 action.selectedProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setSelected(n)));
248 runInsideUIThread(() -> control.setSelected(action.isSelected()));
249 }
250
251 public static void configure(final @Nonnull RadioMenuItem control, final @Nonnull JavaFXAction action) {
252 configure((MenuItem) control, action);
253
254 action.selectedProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setSelected(n)));
255 runInsideUIThread(() -> control.setSelected(action.isSelected()));
256 }
257
258 public static void configure(final @Nonnull MenuItem control, final @Nonnull JavaFXAction action) {
259 requireNonNull(control, ERROR_CONTROL_NULL);
260 requireNonNull(action, ERROR_ACTION_NULL);
261
262 action.onActionProperty().addListener((v, o, n) -> control.setOnAction(n));
263 control.setOnAction(action.getOnAction());
264
265 action.nameProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setText(n)));
266 runInsideUIThread(() -> control.setText(action.getName()));
267
268 action.iconProperty().addListener((v, o, n) -> setIcon(control, n));
269 if (!isBlank(action.getIcon())) {
270 setIcon(control, action.getIcon());
271 }
272
273 action.imageProperty().addListener((v, o, n) -> setGraphic(control, n));
274 if (null != action.getImage()) {
275 setGraphic(control, action.getImage());
276 }
277
278 action.graphicProperty().addListener((v, o, n) -> setGraphic(control, n));
279 if (null != action.getGraphic()) {
280 setGraphic(control, action.getGraphic());
281 }
282
283 action.enabledProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setDisable(!n)));
284 runInsideUIThread(() -> control.setDisable(!action.getEnabled()));
285
286 action.acceleratorProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setAccelerator(n)));
287 runInsideUIThread(() -> control.setAccelerator(action.getAccelerator()));
288
289 action.visibleProperty().addListener((v, o, n) -> runInsideUIThread(() -> control.setVisible(n)));
290 runInsideUIThread(() -> control.setVisible(action.isVisible()));
291
292 action.styleClassProperty().addListener((v, o, n) -> {
293 setStyleClass(control, o, true);
294 setStyleClass(control, n);
295 });
296 setStyleClass(control, action.getStyleClass());
297 }
298
299 public static void setStyleClass(@Nonnull Node node, @Nonnull String styleClass) {
300 setStyleClass(node, styleClass, false);
301 }
302
303 public static void setStyleClass(@Nonnull Node node, @Nonnull String styleClass, boolean remove) {
304 requireNonNull(node, ERROR_CONTROL_NULL);
305 if (isBlank(styleClass)) return;
306
307 ObservableList<String> styleClasses = node.getStyleClass();
308 applyStyleClass(styleClass, styleClasses, remove);
309 }
310
311 public static void setStyleClass(@Nonnull MenuItem node, @Nonnull String styleClass) {
312 setStyleClass(node, styleClass, false);
313 }
314
315 public static void setStyleClass(@Nonnull MenuItem node, @Nonnull String styleClass, boolean remove) {
316 requireNonNull(node, ERROR_CONTROL_NULL);
317 if (isBlank(styleClass)) return;
318 ObservableList<String> styleClasses = node.getStyleClass();
319 applyStyleClass(styleClass, styleClasses, remove);
320 }
321
322 private static void applyStyleClass(String styleClass, ObservableList<String> styleClasses, boolean remove) {
323 runInsideUIThread(() -> {
324 String[] strings = styleClass.split("[,\\ ]");
325 if (remove) {
326 styleClasses.removeAll(strings);
327 } else {
328 Set<String> classes = new LinkedHashSet<>(styleClasses);
329 for (String s : strings) {
330 if (isBlank(s)) continue;
331 classes.add(s.trim());
332 }
333 styleClasses.setAll(classes);
334 }
335 });
336 }
337
338 public static void setTooltip(@Nonnull Control control, @Nullable String text) {
339 runInsideUIThread(() -> {
340 if (isBlank(text)) {
341 return;
342 }
343 requireNonNull(control, ERROR_CONTROL_NULL);
344
345 Tooltip tooltip = control.tooltipProperty().get();
346 if (tooltip == null) {
347 tooltip = new Tooltip();
348 control.tooltipProperty().set(tooltip);
349 }
350 tooltip.setText(text);
351 });
352 }
353
354 public static void setIcon(@Nonnull Labeled control, @Nonnull String iconUrl) {
355 requireNonNull(control, ERROR_CONTROL_NULL);
356 requireNonBlank(iconUrl, ERROR_ICON_BLANK);
357
358 Node graphicNode = resolveIcon(iconUrl);
359 if (graphicNode != null) {
360 runInsideUIThread(() -> control.graphicProperty().set(graphicNode));
361 }
362 }
363
364 public static void setIcon(@Nonnull MenuItem control, @Nonnull String iconUrl) {
365 requireNonNull(control, ERROR_CONTROL_NULL);
366 requireNonBlank(iconUrl, ERROR_ICON_BLANK);
367
368 Node graphicNode = resolveIcon(iconUrl);
369 if (graphicNode != null) {
370 runInsideUIThread(() -> control.graphicProperty().set(graphicNode));
371 }
372 }
373
374 public static void setGraphic(@Nonnull Labeled control, @Nullable Image graphic) {
375 requireNonNull(control, ERROR_CONTROL_NULL);
376
377 runInsideUIThread(() -> {
378 if (graphic != null) {
379 Node graphicNode = new ImageView(graphic);
380 control.graphicProperty().set(graphicNode);
381 } else {
382 control.graphicProperty().set(null);
383 }
384 });
385 }
386
387 public static void setGraphic(@Nonnull MenuItem control, @Nullable Image graphic) {
388 requireNonNull(control, ERROR_CONTROL_NULL);
389
390 runInsideUIThread(() -> {
391 if (graphic != null) {
392 Node graphicNode = new ImageView(graphic);
393 control.graphicProperty().set(graphicNode);
394 } else {
395 control.graphicProperty().set(null);
396 }
397 });
398 }
399
400 public static void setGraphic(@Nonnull Labeled control, @Nullable Node graphic) {
401 requireNonNull(control, ERROR_CONTROL_NULL);
402
403 runInsideUIThread(() -> {
404 if (graphic != null) {
405 control.graphicProperty().set(graphic);
406 } else {
407 control.graphicProperty().set(null);
408 }
409 });
410 }
411
412 public static void setGraphic(@Nonnull MenuItem control, @Nullable Node graphic) {
413 requireNonNull(control, ERROR_CONTROL_NULL);
414
415 runInsideUIThread(() -> {
416 if (graphic != null) {
417 control.graphicProperty().set(graphic);
418 } else {
419 control.graphicProperty().set(null);
420 }
421 });
422 }
423
424 @Nullable
425 public static Node resolveIcon(@Nonnull String iconUrl) {
426 requireNonBlank(iconUrl, ERROR_URL_BLANK);
427
428 if (iconUrl.contains("|")) {
429 // assume classname|arg format
430 return handleAsClassWithArg(iconUrl);
431 } else {
432 URL resource = Thread.currentThread().getContextClassLoader().getResource(iconUrl);
433 if (resource != null) {
434 return new ImageView(new Image(resource.toString()));
435 }
436 }
437 return null;
438 }
439
440 @SuppressWarnings("unchecked")
441 private static Node handleAsClassWithArg(String str) {
442 String[] args = str.split("\\|");
443 if (args.length == 2) {
444 Class<?> iconClass = null;
445 try {
446 iconClass = (Class<?>) JavaFXUtils.class.getClassLoader().loadClass(args[0]);
447 } catch (ClassNotFoundException e) {
448 throw illegalValue(str, Node.class, e);
449 }
450
451 Constructor<?> constructor = null;
452 try {
453 constructor = iconClass.getConstructor(String.class);
454 } catch (NoSuchMethodException e) {
455 throw illegalValue(str, Node.class, e);
456 }
457
458 try {
459 Object o = constructor.newInstance(args[1]);
460 if (o instanceof Node) {
461 return (Node) o;
462 } else if (o instanceof Image) {
463 return new ImageView((Image) o);
464 } else {
465 throw illegalValue(str, Node.class);
466 }
467 } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
468 throw illegalValue(str, Node.class, e);
469 }
470 } else {
471 throw illegalValue(str, Node.class);
472 }
473 }
474
475 @Nullable
476 public static Node findNode(@Nonnull Node root, @Nonnull String id) {
477 requireNonNull(root, ERROR_ROOT_NULL);
478 requireNonBlank(id, ERROR_ID_BLANK);
479
480 if (id.equals(root.getId())) return root;
481
482 if (root instanceof TabPane) {
483 TabPane parent = (TabPane) root;
484 for (Tab child : parent.getTabs()) {
485 if (child.getContent() != null) {
486 Node found = findNode(child.getContent(), id);
487 if (found != null) return found;
488 }
489 }
490 } else if (root instanceof TitledPane) {
491 TitledPane parent = (TitledPane) root;
492 if (parent.getContent() != null) {
493 Node found = findNode(parent.getContent(), id);
494 if (found != null) return found;
495 }
496 } else if (root instanceof Accordion) {
497 Accordion parent = (Accordion) root;
498 for (TitledPane child : parent.getPanes()) {
499 Node found = findNode(child, id);
500 if (found != null) return found;
501 }
502 } else if (root instanceof SplitPane) {
503 SplitPane parent = (SplitPane) root;
504 for (Node child : parent.getItems()) {
505 Node found = findNode(child, id);
506 if (found != null) return found;
507 }
508 } else if (root instanceof ScrollPane) {
509 ScrollPane scrollPane = (ScrollPane) root;
510 if (scrollPane.getContent() != null) {
511 Node found = findNode(scrollPane.getContent(), id);
512 if (found != null) return found;
513 }
514 } else if (root instanceof ToolBar) {
515 ToolBar toolBar = (ToolBar) root;
516 for (Node child : toolBar.getItems()) {
517 Node found = findNode(child, id);
518 if (found != null) return found;
519 }
520 } else if (root instanceof Parent) {
521 Parent parent = (Parent) root;
522 for (Node child : parent.getChildrenUnmodifiable()) {
523 Node found = findNode(child, id);
524 if (found != null) return found;
525 }
526 }
527
528 return null;
529 }
530
531 @Nullable
532 public static Object findElement(@Nonnull Object root, @Nonnull String id) {
533 requireNonNull(root, ERROR_ROOT_NULL);
534 requireNonBlank(id, ERROR_ID_BLANK);
535
536 if (id.equals(getPropertyValue(root, "id"))) return root;
537
538 if (root instanceof ButtonBar) {
539 ButtonBar buttonBar = (ButtonBar) root;
540 for (Node child : buttonBar.getButtons()) {
541 Object found = findElement(child, id);
542 if (found != null) return found;
543 }
544 } else if (root instanceof MenuBar) {
545 MenuBar menuBar = (MenuBar) root;
546 for (Menu child : menuBar.getMenus()) {
547 Object found = findElement(child, id);
548 if (found != null) return found;
549 }
550 } else if (root instanceof ContextMenu) {
551 ContextMenu contextMenu = (ContextMenu) root;
552 for (MenuItem child : contextMenu.getItems()) {
553 Object found = findElement(child, id);
554 if (found != null) return found;
555 }
556 } else if (root instanceof Menu) {
557 Menu menu = (Menu) root;
558 for (MenuItem child : menu.getItems()) {
559 Object found = findElement(child, id);
560 if (found != null) return found;
561 }
562 } else if (root instanceof TabPane) {
563 TabPane tabPane = (TabPane) root;
564 for (Tab child : tabPane.getTabs()) {
565 Object found = findElement(child, id);
566 if (found != null) return found;
567 }
568 } else if (root instanceof Tab) {
569 Tab tab = (Tab) root;
570 if (tab.getContent() != null) {
571 Object found = findElement(tab.getContent(), id);
572 if (found != null) return found;
573 }
574 } else if (root instanceof TitledPane) {
575 TitledPane parent = (TitledPane) root;
576 if (parent.getContent() != null) {
577 Object found = findElement(parent.getContent(), id);
578 if (found != null) return found;
579 }
580 } else if (root instanceof Accordion) {
581 Accordion parent = (Accordion) root;
582 for (TitledPane child : parent.getPanes()) {
583 Object found = findElement(child, id);
584 if (found != null) return found;
585 }
586 } else if (root instanceof SplitPane) {
587 SplitPane parent = (SplitPane) root;
588 for (Node child : parent.getItems()) {
589 Object found = findElement(child, id);
590 if (found != null) return found;
591 }
592 } else if (root instanceof ScrollPane) {
593 ScrollPane scrollPane = (ScrollPane) root;
594 if (scrollPane.getContent() != null) {
595 Object found = findElement(scrollPane.getContent(), id);
596 if (found != null) return found;
597 }
598 } else if (root instanceof ToolBar) {
599 ToolBar toolBar = (ToolBar) root;
600 for (Node child : toolBar.getItems()) {
601 Node found = findNode(child, id);
602 if (found != null) return found;
603 }
604 } else if (root instanceof Parent) {
605 Parent parent = (Parent) root;
606 for (Node child : parent.getChildrenUnmodifiable()) {
607 Object found = findElement(child, id);
608 if (found != null) return found;
609 }
610 }
611
612 return null;
613 }
614
615 @Nullable
616 public static Window getWindowAncestor(@Nonnull Object node) {
617 requireNonNull(node, ERROR_NODE_NULL);
618
619 if (node instanceof Window) {
620 return (Window) node;
621 } else if (node instanceof Scene) {
622 return ((Scene) node).getWindow();
623 } else if (node instanceof Node) {
624 Scene scene = ((Node) node).getScene();
625 if (scene != null) {
626 return scene.getWindow();
627 }
628 } else if (node instanceof Tab) {
629 TabPane tabPane = ((Tab) node).getTabPane();
630 if (tabPane != null) {
631 return getWindowAncestor(tabPane);
632 }
633 }
634
635 return null;
636 }
637
638 private static ValueConversionException illegalValue(Object value, Class<?> klass) {
639 throw new ValueConversionException(value, klass);
640 }
641
642 private static ValueConversionException illegalValue(Object value, Class<?> klass, Exception e) {
643 throw new ValueConversionException(value, klass, e);
644 }
645 }
|