1 /* 2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.property.adapter; 27 28 import com.sun.javafx.property.MethodHelper; 29 import javafx.beans.property.Property; 30 import javafx.beans.property.adapter.ReadOnlyJavaBeanProperty; 31 import javafx.beans.value.ChangeListener; 32 import javafx.beans.value.ObservableValue; 33 34 import java.beans.PropertyChangeEvent; 35 import java.beans.PropertyVetoException; 36 import java.beans.VetoableChangeListener; 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 40 /** 41 */ 42 public class PropertyDescriptor extends ReadOnlyPropertyDescriptor { 43 44 private static final String ADD_VETOABLE_LISTENER_METHOD_NAME = "addVetoableChangeListener"; 45 private static final String REMOVE_VETOABLE_LISTENER_METHOD_NAME = "removeVetoableChangeListener"; 46 private static final String ADD_PREFIX = "add"; 47 private static final String REMOVE_PREFIX = "remove"; 48 private static final String SUFFIX = "Listener"; 49 50 private static final int ADD_VETOABLE_LISTENER_TAKES_NAME = 1; 51 private static final int REMOVE_VETOABLE_LISTENER_TAKES_NAME = 2; 52 53 private final Method setter; 54 private final Method addVetoListener; 55 private final Method removeVetoListener; 56 private final int flags; 57 58 public Method getSetter() {return setter;} 59 60 public PropertyDescriptor(String propertyName, Class<?> beanClass, Method getter, Method setter) { 61 super(propertyName, beanClass, getter); 62 this.setter = setter; 63 64 Method tmpAddVetoListener = null; 65 Method tmpRemoveVetoListener = null; 66 int tmpFlags = 0; 67 68 // reflect addVetoListenerMethod 69 final String addMethodName = ADD_PREFIX + capitalizedName(name) + SUFFIX; 70 try { 71 tmpAddVetoListener = beanClass.getMethod(addMethodName, VetoableChangeListener.class); 72 } catch (NoSuchMethodException e) { 73 try { 74 tmpAddVetoListener = beanClass.getMethod(ADD_VETOABLE_LISTENER_METHOD_NAME, String.class, VetoableChangeListener.class); 75 tmpFlags |= ADD_VETOABLE_LISTENER_TAKES_NAME; 76 } catch (NoSuchMethodException e1) { 77 try { 78 tmpAddVetoListener = beanClass.getMethod(ADD_VETOABLE_LISTENER_METHOD_NAME, VetoableChangeListener.class); 79 } catch (NoSuchMethodException e2) { 80 // ignore 81 } 82 } 83 } 84 85 // reflect removeVetoListenerMethod 86 final String removeMethodName = REMOVE_PREFIX + capitalizedName(name) + SUFFIX; 87 try { 88 tmpRemoveVetoListener = beanClass.getMethod(removeMethodName, VetoableChangeListener.class); 89 } catch (NoSuchMethodException e) { 90 try { 91 tmpRemoveVetoListener = beanClass.getMethod(REMOVE_VETOABLE_LISTENER_METHOD_NAME, String.class, VetoableChangeListener.class); 92 tmpFlags |= REMOVE_VETOABLE_LISTENER_TAKES_NAME; 93 } catch (NoSuchMethodException e1) { 94 try { 95 tmpRemoveVetoListener = beanClass.getMethod(REMOVE_VETOABLE_LISTENER_METHOD_NAME, VetoableChangeListener.class); 96 } catch (NoSuchMethodException e2) { 97 // ignore 98 } 99 } 100 } 101 102 addVetoListener = tmpAddVetoListener; 103 removeVetoListener = tmpRemoveVetoListener; 104 flags = tmpFlags; 105 } 106 107 @Override 108 public void addListener(ReadOnlyListener listener) { 109 super.addListener(listener); 110 if (addVetoListener != null) { 111 try { 112 if ((flags & ADD_VETOABLE_LISTENER_TAKES_NAME) > 0) { 113 addVetoListener.invoke(listener.getBean(), name, listener); 114 } else { 115 addVetoListener.invoke(listener.getBean(), listener); 116 } 117 } catch (IllegalAccessException e) { 118 // ignore 119 } catch (InvocationTargetException e) { 120 // ignore 121 } 122 } 123 } 124 125 126 127 @Override 128 public void removeListener(ReadOnlyListener listener) { 129 super.removeListener(listener); 130 if (removeVetoListener != null) { 131 try { 132 if ((flags & REMOVE_VETOABLE_LISTENER_TAKES_NAME) > 0) { 133 removeVetoListener.invoke(listener.getBean(), name, listener); 134 } else { 135 removeVetoListener.invoke(listener.getBean(), listener); 136 } 137 } catch (IllegalAccessException e) { 138 // ignore 139 } catch (InvocationTargetException e) { 140 // ignore 141 } 142 } 143 } 144 145 public class Listener<T> extends ReadOnlyListener<T> implements ChangeListener<T>, VetoableChangeListener { 146 147 private boolean updating; 148 149 public Listener(Object bean, ReadOnlyJavaBeanProperty<T> property) { 150 super(bean, property); 151 } 152 153 @Override 154 public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) { 155 final ReadOnlyJavaBeanProperty<T> property = checkRef(); 156 if (property == null) { 157 observable.removeListener(this); 158 } else if (!updating) { 159 updating = true; 160 try { 161 MethodHelper.invoke(setter, bean, new Object[] {newValue}); 162 property.fireValueChangedEvent(); 163 } catch (IllegalAccessException e) { 164 // ignore 165 } catch (InvocationTargetException e) { 166 // ignore 167 } finally { 168 updating = false; 169 } 170 } 171 } 172 173 @Override 174 public void vetoableChange(PropertyChangeEvent propertyChangeEvent) throws PropertyVetoException { 175 if (bean.equals(propertyChangeEvent.getSource()) && name.equals(propertyChangeEvent.getPropertyName())) { 176 final ReadOnlyJavaBeanProperty<T> property = checkRef(); 177 if ((property instanceof Property) && (((Property)property).isBound()) && !updating) { 178 throw new PropertyVetoException("A bound value cannot be set.", propertyChangeEvent); 179 } 180 } 181 } 182 } 183 184 }