/** * This file is part of the JCROM project. * Copyright (C) 2008-2014 - All rights reserved. * Authors: Olafur Gauti Gudmundsson, Nicolas Dos Santos * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jcrom.util; import javafx.application.Platform; import javafx.beans.property.*; import javax.jcr.RepositoryException; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; import static org.jcrom.util.ReflectionUtils.getMethod; /** * User: Antoine Mischler <antoine@dooapp.com> * Date: 05/08/2014 * Time: 21:53 */ public class JavaFXUtils { /** * Get the value of the field represented by this {@code Field}, on * the specified object. If the field is a JavaFX property, the value * of the property and not the property itself is automatically returned. */ public static Object getObject(Field field, Object obj) throws IllegalAccessException { if (javafx.beans.property.Property.class.isAssignableFrom(field.getType())) { return getPropertyValue(field, obj); } else { return field.get(obj); } } /** * This method tries to access to the value of a JavaFX property field using the following strategy: * it first tries to read directly the property value (e.g. name.getValue()). If the property is null * we try the retrieve the value getter for this field (e.g. getName) to read the value. If the getter * does not exist, we try to retrieve the property getter (e.g. nameProperty()) to access the property * and then to get its value. */ private static Object getPropertyValue(Field field, Object obj) throws IllegalAccessException { Property property = (Property) field.get(obj); if (property != null) { return property.getValue(); } else { try { Method getter = getMethod(obj.getClass(), "get" + capitalize(field.getName())); return getter.invoke(obj); } catch (NoSuchMethodException e) { try { property = getPropertyByPropertyGetter(field, obj); return property.getValue(); } catch (NoSuchMethodException e1) { return null; } catch (InvocationTargetException e1) { return null; } } catch (InvocationTargetException e) { return null; } } } private static String capitalize(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Set the value of a JavaFX property field by applying the following strategy: * If the field is not null, the value is set directly on the property (e.g. name.setValue("")). * If the property is null, i.e. it has not been initialized, we try to retrieve the value setter for * this field (e.g. setName()) to set the value. If the setter * does not exist, we try to retrieve the property getter (e.g. nameProperty()) to access the property * and then to set its value. */ private static void setPropertyValue(Field field, final Object obj, final Object value) throws IllegalAccessException { Property property = (Property) field.get(obj); if (property != null) { updateProperty(value, property); } else { try { final Method setter = getMethod(obj.getClass(), "set" + capitalize(field.getName()), value.getClass()); invokeSetter(obj, value, setter); } catch (NoSuchMethodException e) { try { property = getPropertyByPropertyGetter(field, obj); updateProperty(value, property); } catch (NoSuchMethodException e1) { e1.printStackTrace(); } catch (InvocationTargetException e1) { e1.printStackTrace(); } } } } /** * Invoke the setter of a property in the current thread and then in the JavaFX thread. * Actually, if a visible node of the JavaFX scene graph is bound to the property, it * will be only possible to update its value in the JavaFX thread. */ private static void invokeSetter(final Object obj, final Object value, final Method setter) { try { setter.invoke(obj, value); } catch (RuntimeException e) { Platform.runLater(new Runnable() { @Override public void run() { try { setter.invoke(obj, value); } catch (Exception e) { e.printStackTrace(); } } }); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * Set the value of a JavaFX property in the current thread and then in the JavaFX thread. * Actually, if a visible node of the JavaFX scene graph is bound to the property, it * will be only possible to update its value in the JavaFX thread. * <p/> * If the property is bound, this method does nothing. */ private static void updateProperty(final Object value, final Property finalProperty) { if (finalProperty.isBound()) { System.out.println("Impossible to set the value " + value + " to " + finalProperty.getName() + ". Property is bound."); return; } try { finalProperty.setValue(value); } catch (RuntimeException e) { Platform.runLater(new Runnable() { @Override public void run() { finalProperty.setValue(value); } }); } } private static Property getPropertyByPropertyGetter(Field field, Object obj) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Property property; Method propertyGetter = getMethod(obj.getClass(), field.getName() + "Property"); property = (Property) propertyGetter.invoke(obj); return property; } /** * Sets the field represented by this {@code Field} object on the * specified object argument to the specified new value. If the field * is a JavaFX Property, then the value of the property will be set and not * the property itself. */ public static void setObject(Field field, Object source, Object value) throws IllegalAccessException { if (value == null) return; if (MapProperty.class.isAssignableFrom(field.getType())) { Object mapProperty = field.get(source); if (mapProperty != null) { ((MapProperty) field.get(source)).putAll((Map) value); } else { try { ((MapProperty) getPropertyByPropertyGetter(field, source)).putAll((Map) value); } catch (NoSuchMethodException e) { } catch (InvocationTargetException e) { } } } else if (ListProperty.class.isAssignableFrom(field.getType())) { Object listProperty = field.get(source); if (listProperty != null) { ((ListProperty) field.get(source)).setAll((Collection) value); } else { try { ((ListProperty) getPropertyByPropertyGetter(field, source)).setAll((Collection) value); } catch (NoSuchMethodException e) { } catch (InvocationTargetException e) { } } } else if (javafx.beans.property.Property.class.isAssignableFrom(field.getType())) { setPropertyValue(field, source, value); } else { field.set(source, value); } } /** * Check if this field is a Map or a JavaFX MapProperty. */ public static boolean isMap(Field field) { return ReflectionUtils.implementsInterface(field.getType(), Map.class) || MapProperty.class.isAssignableFrom(field.getType()); } /** * Check if this field is a List or a JavaFX ListProperty. */ public static boolean isList(Field field) { return isList(field.getType()); } /** * Check if this class is a subclass of List or JavaFX ListProperty. */ public static boolean isList(Class<?> clazz) { return ReflectionUtils.implementsInterface(clazz, List.class) || ListProperty.class.isAssignableFrom(clazz); } public static boolean isNotString(Field field) { return field.getType() != String.class && field.getType() != StringProperty.class; } /** * Returns a representation of the Value of the given Property by using the given field's {@link Class}. * If the field is an ObjectProperty, then the parametrized type of this ObjectProperty will be used. */ public static Object getValue(Field field, Object obj, javax.jcr.Property p) throws IllegalAccessException, RepositoryException, IOException { if (ObjectProperty.class.isAssignableFrom(field.getType())) { return JcrUtils.getValue(ReflectionUtils.getObjectPropertyGeneric(obj, field), p.getValue()); } else { return JcrUtils.getValue(field.getType(), p.getValue()); } } /** * Returns the type of a field. If the field is an ObjectProperty, * then the parametrized type of this ObjectProperty is returned, i.e. * the type of its value. */ public static Class<?> getType(Field field, Object source) { if (ObjectProperty.class.isAssignableFrom(field.getType())) { return ReflectionUtils.getObjectPropertyGeneric(source, field); } else { return field.getType(); } } }