Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.beans.property.adapter;
027
028import com.sun.javafx.binding.ExpressionHelper;
029import com.sun.javafx.property.adapter.PropertyDescriptor;
030import javafx.beans.InvalidationListener;
031import javafx.beans.property.ObjectProperty;
032import javafx.beans.value.ChangeListener;
033import javafx.beans.value.ObservableValue;
034
035import java.lang.reflect.InvocationTargetException;
036import java.lang.reflect.UndeclaredThrowableException;
037import sun.misc.Cleaner;
038
039import java.security.AccessController;
040import java.security.AccessControlContext;
041import java.security.PrivilegedAction;
042
043import sun.reflect.misc.MethodUtil;
044
045/**
046 * A {@code JavaBeanObjectProperty} provides an adapter between a regular
047 * Java Bean property of type {@code T} and a JavaFX 
048 * {@code ObjectProperty<T>}. It cannot be created directly, but a 
049 * {@link JavaBeanObjectPropertyBuilder} has to be used.
050 * <p>
051 * As a minimum, the Java Bean must implement a getter and a setter for the
052 * property. If the getter of an instance of this class is called, the property of 
053 * the Java Bean is returned. If the setter is called, the value will be passed
054 * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
055 * PropertyChangeListeners), this {@code JavaBeanObjectProperty} will be 
056 * aware of changes in the Java Bean. Otherwise it can be notified about
057 * changes by calling {@link #fireValueChangedEvent()}. If the Java Bean property 
058 * is also constrained (i.e. it supports VetoableChangeListeners), this 
059 * {@code JavaBeanObjectProperty} will reject changes, if it is bound to an 
060 * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Object&gt;}.
061 * 
062 * @see javafx.beans.property.ObjectProperty
063 * @see JavaBeanObjectPropertyBuilder
064 * 
065 * @param T type of the wrapped {@code Object}
066 */
067public final class JavaBeanObjectProperty<T> extends ObjectProperty<T> implements JavaBeanProperty<T> {
068
069    private final PropertyDescriptor descriptor;
070    private final PropertyDescriptor.Listener<T> listener;
071
072    private ObservableValue<? extends T> observable = null;
073    private ExpressionHelper<T> helper = null;
074
075    private final AccessControlContext acc = AccessController.getContext();
076
077    JavaBeanObjectProperty(PropertyDescriptor descriptor, Object bean) {
078        this.descriptor = descriptor;
079        this.listener = descriptor.new Listener<T>(bean, this);
080        descriptor.addListener(listener);
081        Cleaner.create(this, new Runnable() {
082            @Override
083            public void run() {
084                JavaBeanObjectProperty.this.descriptor.removeListener(listener);
085            }
086        });
087    }
088
089    /**
090     * {@inheritDoc}
091     * 
092     * @throws UndeclaredThrowableException if calling the getter of the Java Bean
093     * property throws an {@code IllegalAccessException} or an 
094     * {@code InvocationTargetException}.
095     */
096    @SuppressWarnings("unchecked")
097    @Override
098    public T get() {
099        return AccessController.doPrivileged(new PrivilegedAction<T>() {
100            public T run() {
101                try {
102                    return (T)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
103                } catch (IllegalAccessException e) {
104                    throw new UndeclaredThrowableException(e);
105                } catch (InvocationTargetException e) {
106                    throw new UndeclaredThrowableException(e);
107                }
108            }
109        }, acc);
110    }
111
112    /**
113     * {@inheritDoc}
114     * 
115     * @throws UndeclaredThrowableException if calling the getter of the Java Bean
116     * property throws an {@code IllegalAccessException} or an 
117     * {@code InvocationTargetException}.
118     */
119    @Override
120    public void set(final T value) {
121        if (isBound()) {
122            throw new RuntimeException("A bound value cannot be set.");
123        }
124
125        AccessController.doPrivileged(new PrivilegedAction<Void>() {
126            public Void run() {
127                try {
128                    MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
129                    ExpressionHelper.fireValueChangedEvent(helper);
130                } catch (IllegalAccessException e) {
131                    throw new UndeclaredThrowableException(e);
132                } catch (InvocationTargetException e) {
133                    throw new UndeclaredThrowableException(e);
134                }
135                return null;
136            }
137        }, acc);
138    }
139
140
141    /**
142     * {@inheritDoc}
143     */
144    @Override
145    public void bind(ObservableValue<? extends T> observable) {
146        if (observable == null) {
147            throw new NullPointerException("Cannot bind to null");
148        }
149
150        if (!observable.equals(this.observable)) {
151            unbind();
152            set(observable.getValue());
153            this.observable = observable;
154            this.observable.addListener(listener);
155        }
156    }
157
158    /**
159     * {@inheritDoc}
160     */
161    @Override
162    public void unbind() {
163        if (observable != null) {
164            observable.removeListener(listener);
165            observable = null;
166        }
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public boolean isBound() {
174        return observable != null;
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public Object getBean() {
182        return listener.getBean();
183    }
184
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public String getName() {
190        return descriptor.getName();
191    }
192
193    /**
194     * {@inheritDoc}
195     */
196    @Override
197    public void addListener(ChangeListener<? super T> listener) {
198        helper = ExpressionHelper.addListener(helper, this, listener);
199    }
200
201    /**
202     * {@inheritDoc}
203     */
204    @Override
205    public void removeListener(ChangeListener<? super T> listener) {
206        helper = ExpressionHelper.removeListener(helper, listener);
207    }
208
209    /**
210     * {@inheritDoc}
211     */
212    @Override
213    public void addListener(InvalidationListener listener) {
214        helper = ExpressionHelper.addListener(helper, this, listener);
215    }
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public void removeListener(InvalidationListener listener) {
222        helper = ExpressionHelper.removeListener(helper, listener);
223    }
224
225    /**
226     * {@inheritDoc}
227     */
228    @Override
229    public void fireValueChangedEvent() {
230        ExpressionHelper.fireValueChangedEvent(helper);
231    }
232
233    /**
234     * {@inheritDoc}
235     */
236    @Override
237    public void dispose() {
238        descriptor.removeListener(listener);
239
240    }
241
242    /**
243     * Returns a string representation of this {@code JavaBeanObjectProperty} object.
244     * @return a string representation of this {@code JavaBeanObjectProperty} object.
245     */
246    @Override
247    public String toString() {
248        final Object bean = getBean();
249        final String name = getName();
250        final StringBuilder result = new StringBuilder("ObjectProperty [");
251        if (bean != null) {
252            result.append("bean: ").append(bean).append(", ");
253        }
254        if ((name != null) && (!name.equals(""))) {
255            result.append("name: ").append(name).append(", ");
256        }
257        if (isBound()) {
258            result.append("bound, ");
259        }
260        result.append("value: ").append(get());
261        result.append("]");
262        return result.toString();
263    }
264}