1 /* 2 * Copyright (c) 2011, 2013, 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 javafx.beans.property.Property; 29 import javafx.beans.property.adapter.ReadOnlyJavaBeanProperty; 30 import javafx.beans.value.ChangeListener; 31 import javafx.beans.value.ObservableValue; 32 33 import java.beans.PropertyChangeEvent; 34 import java.beans.PropertyVetoException; 35 import java.beans.VetoableChangeListener; 36 import java.lang.reflect.InvocationTargetException; 37 import java.lang.reflect.Method; 38 39 import sun.reflect.misc.MethodUtil; 40 41 /** 42 */ 43 public class PropertyDescriptor extends ReadOnlyPropertyDescriptor { 44 45 private static final String ADD_VETOABLE_LISTENER_METHOD_NAME = "addVetoableChangeListener"; 46 private static final String REMOVE_VETOABLE_LISTENER_METHOD_NAME = "removeVetoableChangeListener"; 47 private static final String ADD_PREFIX = "add"; 48 private static final String REMOVE_PREFIX = "remove"; 49 private static final String SUFFIX = "Listener"; 50 51 private static final int ADD_VETOABLE_LISTENER_TAKES_NAME = 1; 52 private static final int REMOVE_VETOABLE_LISTENER_TAKES_NAME = 2; 53 54 private final Method setter; 55 private final Method addVetoListener; 56 private final Method removeVetoListener; 57 private final int flags; 58 59 public Method getSetter() {return setter;} 60 61 public PropertyDescriptor(String propertyName, Class<?> beanClass, Method getter, Method setter) { 62 super(propertyName, beanClass, getter); 63 this.setter = setter; 64 65 Method tmpAddVetoListener = null; 66 Method tmpRemoveVetoListener = null; 67 int tmpFlags = 0; 68 69 // reflect addVetoListenerMethod 70 final String addMethodName = ADD_PREFIX + capitalizedName(name) + SUFFIX; 71 try { 72 tmpAddVetoListener = beanClass.getMethod(addMethodName, VetoableChangeListener.class); 73 } catch (NoSuchMethodException e) { 74 try { 75 tmpAddVetoListener = beanClass.getMethod(ADD_VETOABLE_LISTENER_METHOD_NAME, String.class, VetoableChangeListener.class); 76 tmpFlags |= ADD_VETOABLE_LISTENER_TAKES_NAME; 77 } catch (NoSuchMethodException e1) { 78 try { 79 tmpAddVetoListener = beanClass.getMethod(ADD_VETOABLE_LISTENER_METHOD_NAME, VetoableChangeListener.class); 80 } catch (NoSuchMethodException e2) { 81 // ignore 82 } 83 } 84 } 85 86 // reflect removeVetoListenerMethod 87 final String removeMethodName = REMOVE_PREFIX + capitalizedName(name) + SUFFIX; 88 try { 89 tmpRemoveVetoListener = beanClass.getMethod(removeMethodName, VetoableChangeListener.class); 90 } catch (NoSuchMethodException e) { 91 try { 92 tmpRemoveVetoListener = beanClass.getMethod(REMOVE_VETOABLE_LISTENER_METHOD_NAME, String.class, VetoableChangeListener.class); 93 tmpFlags |= REMOVE_VETOABLE_LISTENER_TAKES_NAME; 94 } catch (NoSuchMethodException e1) { 95 try { 96 tmpRemoveVetoListener = beanClass.getMethod(REMOVE_VETOABLE_LISTENER_METHOD_NAME, VetoableChangeListener.class); 97 } catch (NoSuchMethodException e2) { 98 // ignore 99 } 100 } 101 } 102 103 addVetoListener = tmpAddVetoListener; 104 removeVetoListener = tmpRemoveVetoListener; 105 flags = tmpFlags; 106 } 107 108 @Override 109 public void addListener(ReadOnlyListener listener) { 110 super.addListener(listener); 111 if (addVetoListener != null) { 112 try { 113 if ((flags & ADD_VETOABLE_LISTENER_TAKES_NAME) > 0) { 114 addVetoListener.invoke(listener.getBean(), name, listener); 115 } else { 116 addVetoListener.invoke(listener.getBean(), listener); 117 } 118 } catch (IllegalAccessException e) { 119 // ignore 120 } catch (InvocationTargetException e) { 121 // ignore 122 } 123 } 124 } 125 126 127 128 @Override 129 public void removeListener(ReadOnlyListener listener) { 130 super.removeListener(listener); 131 if (removeVetoListener != null) { 132 try { 133 if ((flags & REMOVE_VETOABLE_LISTENER_TAKES_NAME) > 0) { 134 removeVetoListener.invoke(listener.getBean(), name, listener); 135 } else { 136 removeVetoListener.invoke(listener.getBean(), listener); 137 } 138 } catch (IllegalAccessException e) { 139 // ignore 140 } catch (InvocationTargetException e) { 141 // ignore 142 } 143 } 144 } 145 146 public class Listener<T> extends ReadOnlyListener<T> implements ChangeListener<T>, VetoableChangeListener { 147 148 private boolean updating; 149 150 public Listener(Object bean, ReadOnlyJavaBeanProperty<T> property) { 151 super(bean, property); 152 } 153 154 @Override 155 public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) { 156 final ReadOnlyJavaBeanProperty<T> property = checkRef(); 157 if (property == null) { 158 observable.removeListener(this); 159 } else if (!updating) { 160 updating = true; 161 try { 162 MethodUtil.invoke(setter, bean, new Object[] {newValue}); 163 property.fireValueChangedEvent(); 164 } catch (IllegalAccessException e) { 165 // ignore 166 } catch (InvocationTargetException e) { 167 // ignore 168 } finally { 169 updating = false; 170 } 171 } 172 } 173 174 @Override 175 public void vetoableChange(PropertyChangeEvent propertyChangeEvent) throws PropertyVetoException { 176 if (bean.equals(propertyChangeEvent.getSource()) && name.equals(propertyChangeEvent.getPropertyName())) { 177 final ReadOnlyJavaBeanProperty<T> property = checkRef(); 178 if ((property instanceof Property) && (((Property)property).isBound()) && !updating) { 179 throw new PropertyVetoException("A bound value cannot be set.", propertyChangeEvent); 180 } 181 } 182 } 183 } 184 185 }