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