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 javafx.beans.property.adapter;
  27 
  28 import com.sun.javafx.binding.ExpressionHelper;
  29 import com.sun.javafx.property.MethodHelper;
  30 import com.sun.javafx.property.adapter.Disposer;
  31 import com.sun.javafx.property.adapter.PropertyDescriptor;
  32 import javafx.beans.InvalidationListener;
  33 import javafx.beans.property.StringProperty;
  34 import javafx.beans.value.ChangeListener;
  35 import javafx.beans.value.ObservableValue;
  36 
  37 import java.lang.reflect.InvocationTargetException;
  38 import java.lang.reflect.UndeclaredThrowableException;
  39 
  40 import java.security.AccessController;
  41 import java.security.AccessControlContext;
  42 import java.security.PrivilegedAction;
  43 
  44 /**
  45  * A {@code JavaBeanStringProperty} provides an adapter between a regular
  46  * Java Bean property of type {@code String} and a JavaFX
  47  * {@code StringProperty}. It cannot be created directly, but a
  48  * {@link JavaBeanStringPropertyBuilder} has to be used.
  49  * <p>
  50  * As a minimum, the Java Bean class must implement a getter and a setter for the
  51  * property. If the getter of an instance of this class is called, the property of
  52  * the Java Bean is returned. If the setter is called, the value will be passed
  53  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  54  * PropertyChangeListeners), this {@code JavaBeanStringProperty} will be
  55  * aware of changes in the Java Bean. Otherwise it can be notified about
  56  * changes by calling {@link #fireValueChangedEvent()}. If the Java Bean property
  57  * is also constrained (i.e. it supports VetoableChangeListeners), this
  58  * {@code JavaBeanStringProperty} will reject changes, if it is bound to an
  59  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;String&gt;}.
  60  * </p>
  61  * <p>
  62  * The Java Bean class must be declared public. If that class is in a named
  63  * module, then the module must {@link Module#isOpen(String,Module) open}
  64  * the containing package to at least the {@code javafx.base} module
  65  * (or {@link Module#isExported(String) export} the containing package
  66  * unconditionally).
  67  * </p>
  68  *
  69  * @see javafx.beans.property.StringProperty
  70  * @see JavaBeanStringPropertyBuilder
  71  * @since JavaFX 2.1
  72  */
  73 public final class JavaBeanStringProperty extends StringProperty implements JavaBeanProperty<String> {
  74 
  75     private final PropertyDescriptor descriptor;
  76     private final PropertyDescriptor.Listener<String> listener;
  77 
  78     private ObservableValue<? extends String> observable = null;
  79     private ExpressionHelper<String> helper = null;
  80 
  81     private final AccessControlContext acc = AccessController.getContext();
  82 
  83     JavaBeanStringProperty(PropertyDescriptor descriptor, Object bean) {
  84         this.descriptor = descriptor;
  85         this.listener = descriptor.new Listener<String>(bean, this);
  86         descriptor.addListener(listener);
  87         Disposer.addRecord(this, new DescriptorListenerCleaner(descriptor, listener));
  88     }
  89 
  90     /**
  91      * {@inheritDoc}
  92      *
  93      * @throws UndeclaredThrowableException if calling the getter of the Java Bean
  94      * property throws an {@code IllegalAccessException} or an
  95      * {@code InvocationTargetException}.
  96      */
  97     @Override
  98     public String get() {
  99         return AccessController.doPrivileged((PrivilegedAction<String>) () -> {
 100             try {
 101                 return (String)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
 102             } catch (IllegalAccessException e) {
 103                 throw new UndeclaredThrowableException(e);
 104             } catch (InvocationTargetException e) {
 105                 throw new UndeclaredThrowableException(e);
 106             }
 107         }, acc);
 108     }
 109 
 110     /**
 111      * {@inheritDoc}
 112      *
 113      * @throws UndeclaredThrowableException if calling the getter of the Java Bean
 114      * property throws an {@code IllegalAccessException} or an
 115      * {@code InvocationTargetException}.
 116      */
 117     @Override
 118     public void set(final String value) {
 119         if (isBound()) {
 120             throw new RuntimeException("A bound value cannot be set.");
 121         }
 122         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 123             try {
 124                 MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
 125                 ExpressionHelper.fireValueChangedEvent(helper);
 126             } catch (IllegalAccessException e) {
 127                 throw new UndeclaredThrowableException(e);
 128             } catch (InvocationTargetException e) {
 129                 throw new UndeclaredThrowableException(e);
 130             }
 131             return null;
 132         }, acc);
 133     }
 134 
 135     /**
 136      * {@inheritDoc}
 137      */
 138     @Override
 139     public void bind(ObservableValue<? extends String> observable) {
 140         if (observable == null) {
 141             throw new NullPointerException("Cannot bind to null");
 142         }
 143 
 144         if (!observable.equals(this.observable)) {
 145             unbind();
 146             set(observable.getValue());
 147             this.observable = observable;
 148             this.observable.addListener(listener);
 149         }
 150     }
 151 
 152     /**
 153      * {@inheritDoc}
 154      */
 155     @Override
 156     public void unbind() {
 157         if (observable != null) {
 158             observable.removeListener(listener);
 159             observable = null;
 160         }
 161     }
 162 
 163     /**
 164      * {@inheritDoc}
 165      */
 166     @Override
 167     public boolean isBound() {
 168         return observable != null;
 169     }
 170 
 171     /**
 172      * {@inheritDoc}
 173      */
 174     @Override
 175     public Object getBean() {
 176         return listener.getBean();
 177     }
 178 
 179     /**
 180      * {@inheritDoc}
 181      */
 182     @Override
 183     public String getName() {
 184         return descriptor.getName();
 185     }
 186 
 187     /**
 188      * {@inheritDoc}
 189      */
 190     @Override
 191     public void addListener(ChangeListener<? super String> listener) {
 192         helper = ExpressionHelper.addListener(helper, this, listener);
 193     }
 194 
 195     /**
 196      * {@inheritDoc}
 197      */
 198     @Override
 199     public void removeListener(ChangeListener<? super String> listener) {
 200         helper = ExpressionHelper.removeListener(helper, listener);
 201     }
 202 
 203     /**
 204      * {@inheritDoc}
 205      */
 206     @Override
 207     public void addListener(InvalidationListener listener) {
 208         helper = ExpressionHelper.addListener(helper, this, listener);
 209     }
 210 
 211     /**
 212      * {@inheritDoc}
 213      */
 214     @Override
 215     public void removeListener(InvalidationListener listener) {
 216         helper = ExpressionHelper.removeListener(helper, listener);
 217     }
 218 
 219     /**
 220      * {@inheritDoc}
 221      */
 222     @Override
 223     public void fireValueChangedEvent() {
 224         ExpressionHelper.fireValueChangedEvent(helper);
 225     }
 226 
 227     /**
 228      * {@inheritDoc}
 229      */
 230     @Override
 231     public void dispose() {
 232         descriptor.removeListener(listener);
 233 
 234     }
 235 }