1 /* 2 * Copyright (c) 2011, 2018, 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.WeakListener; 29 import javafx.beans.property.adapter.ReadOnlyJavaBeanProperty; 30 31 import java.beans.PropertyChangeEvent; 32 import java.beans.PropertyChangeListener; 33 import java.lang.ref.WeakReference; 34 import java.lang.reflect.InvocationTargetException; 35 import java.lang.reflect.Method; 36 37 import com.sun.javafx.reflect.ReflectUtil; 38 39 import static java.util.Locale.ENGLISH; 40 41 /** 42 */ 43 public class ReadOnlyPropertyDescriptor { 44 45 private static final String ADD_LISTENER_METHOD_NAME = "addPropertyChangeListener"; 46 private static final String REMOVE_LISTENER_METHOD_NAME = "removePropertyChangeListener"; 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_LISTENER_TAKES_NAME = 1; 52 private static final int REMOVE_LISTENER_TAKES_NAME = 2; 53 54 protected final String name; 55 protected final Class<?> beanClass; 56 private final Method getter; 57 private final Class<?> type; 58 59 private final Method addChangeListener; 60 private final Method removeChangeListener; 61 private final int flags; 62 63 public String getName() {return name;} 64 public Method getGetter() {return getter;} 65 public Class<?> getType() {return type;} 66 67 public ReadOnlyPropertyDescriptor(String propertyName, Class<?> beanClass, Method getter) { 68 ReflectUtil.checkPackageAccess(beanClass); 69 70 this.name = propertyName; 71 this.beanClass = beanClass; 72 this.getter = getter; 73 this.type = getter.getReturnType(); 74 75 Method tmpAddChangeListener = null; 76 Method tmpRemoveChangeListener = null; 77 int tmpFlags = 0; 78 79 try { 80 final String methodName = ADD_PREFIX + capitalizedName(name) + SUFFIX; 81 tmpAddChangeListener = beanClass.getMethod(methodName, PropertyChangeListener.class); 82 } catch (NoSuchMethodException e) { 83 try { 84 tmpAddChangeListener = beanClass.getMethod(ADD_LISTENER_METHOD_NAME, String.class, PropertyChangeListener.class); 85 tmpFlags |= ADD_LISTENER_TAKES_NAME; 86 } catch (NoSuchMethodException e1) { 87 try { 88 tmpAddChangeListener = beanClass.getMethod(ADD_LISTENER_METHOD_NAME, PropertyChangeListener.class); 89 } catch (NoSuchMethodException e2) { 90 // ignore 91 } 92 } 93 } 94 95 try { 96 final String methodName = REMOVE_PREFIX + capitalizedName(name) + SUFFIX; 97 tmpRemoveChangeListener = beanClass.getMethod(methodName, PropertyChangeListener.class); 98 } catch (NoSuchMethodException e) { 99 try { 100 tmpRemoveChangeListener = beanClass.getMethod(REMOVE_LISTENER_METHOD_NAME, String.class, PropertyChangeListener.class); 101 tmpFlags |= REMOVE_LISTENER_TAKES_NAME; 102 } catch (NoSuchMethodException e1) { 103 try { 104 tmpRemoveChangeListener = beanClass.getMethod(REMOVE_LISTENER_METHOD_NAME, PropertyChangeListener.class); 105 } catch (NoSuchMethodException e2) { 106 // ignore 107 } 108 } 109 } 110 111 addChangeListener = tmpAddChangeListener; 112 removeChangeListener = tmpRemoveChangeListener; 113 flags = tmpFlags; 114 } 115 116 public static String capitalizedName(String name) { 117 return ((name == null) || (name.length() == 0))? name : name.substring(0, 1).toUpperCase(ENGLISH) + name.substring(1); 118 } 119 120 public void addListener(ReadOnlyListener listener) { 121 if (addChangeListener != null) { 122 try { 123 if ((flags & ADD_LISTENER_TAKES_NAME) > 0) { 124 addChangeListener.invoke(listener.getBean(), name, listener); 125 } else { 126 addChangeListener.invoke(listener.getBean(), listener); 127 } 128 } catch (IllegalAccessException e) { 129 // ignore 130 } catch (InvocationTargetException e) { 131 // ignore 132 } 133 } 134 } 135 136 public void removeListener(ReadOnlyListener listener) { 137 if (removeChangeListener != null) { 138 try { 139 if ((flags & REMOVE_LISTENER_TAKES_NAME) > 0) { 140 removeChangeListener.invoke(listener.getBean(), name, listener); 141 } else { 142 removeChangeListener.invoke(listener.getBean(), listener); 143 } 144 } catch (IllegalAccessException e) { 145 // ignore 146 } catch (InvocationTargetException e) { 147 // ignore 148 } 149 } 150 } 151 152 public class ReadOnlyListener<T> implements PropertyChangeListener, WeakListener { 153 154 protected final Object bean; 155 private final WeakReference<ReadOnlyJavaBeanProperty<T>> propertyRef; 156 157 public Object getBean() {return bean;} 158 159 public ReadOnlyListener(Object bean, ReadOnlyJavaBeanProperty<T> property) { 160 this.bean = bean; 161 this.propertyRef = new WeakReference<ReadOnlyJavaBeanProperty<T>>(property); 162 } 163 164 protected ReadOnlyJavaBeanProperty<T> checkRef() { 165 final ReadOnlyJavaBeanProperty<T> result = propertyRef.get(); 166 if (result == null) { 167 removeListener(this); 168 } 169 return result; 170 } 171 172 @Override 173 public void propertyChange(PropertyChangeEvent propertyChangeEvent) { 174 if (bean.equals(propertyChangeEvent.getSource()) && name.equals(propertyChangeEvent.getPropertyName())) { 175 final ReadOnlyJavaBeanProperty<T> property = checkRef(); 176 if (property != null) { 177 property.fireValueChangedEvent(); 178 } 179 } 180 } 181 182 @Override 183 public boolean wasGarbageCollected() { 184 return checkRef() == null; 185 } 186 } 187 }