< prev index next >

modules/javafx.base/src/main/java/com/sun/javafx/binding/ExpressionHelper.java

Print this page

  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.binding;
 27 




 28 import javafx.beans.InvalidationListener;
 29 import javafx.beans.value.ChangeListener;
 30 import javafx.beans.value.ObservableValue;
 31 
 32 import java.util.Arrays;
 33 
 34 /**
 35  * A convenience class for creating implementations of {@link javafx.beans.value.ObservableValue}.
 36  * It contains all of the infrastructure support for value invalidation- and
 37  * change event notification.
 38  *
 39  * This implementation can handle adding and removing listeners while the
 40  * observers are being notified, but it is not thread-safe.
 41  *
 42  *
 43  */
 44 public abstract class ExpressionHelper<T> extends ExpressionHelperBase {
 45 
 46     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 47     // Static methods
 48 
 49     public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, InvalidationListener listener) {
 50         if ((observable == null) || (listener == null)) {
 51             throw new NullPointerException();
 52         }
 53         observable.getValue(); // validate observable

171             return (listener.equals(this.listener))? null : this;
172         }
173 
174         @Override
175         protected void fireValueChangedEvent() {
176             final T oldValue = currentValue;
177             currentValue = observable.getValue();
178             final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
179             if (changed) {
180                 try {
181                     listener.changed(observable, oldValue, currentValue);
182                 } catch (Exception e) {
183                     Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
184                 }
185             }
186         }
187     }
188 
189     private static class Generic<T> extends ExpressionHelper<T> {
190 
191         private InvalidationListener[] invalidationListeners;
192         private ChangeListener<? super T>[] changeListeners;
193         private int invalidationSize;
194         private int changeSize;
195         private boolean locked;
196         private T currentValue;


197 
198         private Generic(ObservableValue<T> observable, InvalidationListener listener0, InvalidationListener listener1) {
199             super(observable);
200             this.invalidationListeners = new InvalidationListener[] {listener0, listener1};
201             this.invalidationSize = 2;

202         }
203 
204         private Generic(ObservableValue<T> observable, ChangeListener<? super T> listener0, ChangeListener<? super T> listener1) {
205             super(observable);
206             this.changeListeners = new ChangeListener[] {listener0, listener1};
207             this.changeSize = 2;

208             this.currentValue = observable.getValue();
209         }
210 
211         private Generic(ObservableValue<T> observable, InvalidationListener invalidationListener, ChangeListener<? super T> changeListener) {
212             super(observable);
213             this.invalidationListeners = new InvalidationListener[] {invalidationListener};
214             this.invalidationSize = 1;
215             this.changeListeners = new ChangeListener[] {changeListener};
216             this.changeSize = 1;
217             this.currentValue = observable.getValue();
218         }
219 
220         @Override
221         protected Generic<T> addListener(InvalidationListener listener) {
222             if (invalidationListeners == null) {
223                 invalidationListeners = new InvalidationListener[] {listener};
224                 invalidationSize = 1;
225             } else {
226                 final int oldCapacity = invalidationListeners.length;
227                 if (locked) {
228                     final int newCapacity = (invalidationSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1;
229                     invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity);
230                 } else if (invalidationSize == oldCapacity) {
231                     invalidationSize = trim(invalidationSize, invalidationListeners);
232                     if (invalidationSize == oldCapacity) {
233                         final int newCapacity = (oldCapacity * 3)/2 + 1;
234                         invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity);
235                     }
236                 }
237                 invalidationListeners[invalidationSize++] = listener;
238             }

239             return this;
240         }
241 
242         @Override
243         protected ExpressionHelper<T> removeListener(InvalidationListener listener) {
244             if (invalidationListeners != null) {
245                 for (int index = 0; index < invalidationSize; index++) {
246                     if (listener.equals(invalidationListeners[index])) {
247                         if (invalidationSize == 1) {
248                             if (changeSize == 1) {
249                                 return new SingleChange<T>(observable, changeListeners[0]);
250                             }
251                             invalidationListeners = null;
252                             invalidationSize = 0;
253                         } else if ((invalidationSize == 2) && (changeSize == 0)) {
254                             return new SingleInvalidation<T>(observable, invalidationListeners[1-index]);
255                         } else {
256                             final int numMoved = invalidationSize - index - 1;
257                             final InvalidationListener[] oldListeners = invalidationListeners;
258                             if (locked) {
259                                 invalidationListeners = new InvalidationListener[invalidationListeners.length];
260                                 System.arraycopy(oldListeners, 0, invalidationListeners, 0, index);
261                             }
262                             if (numMoved > 0) {
263                                 System.arraycopy(oldListeners, index+1, invalidationListeners, index, numMoved);
264                             }
265                             invalidationSize--;
266                             if (!locked) {
267                                 invalidationListeners[invalidationSize] = null; // Let gc do its work
268                             }
269                         }
270                         break;
271                     }
272                 }
273             }
274             return this;
275         }
276 
277         @Override
278         protected ExpressionHelper<T> addListener(ChangeListener<? super T> listener) {
279             if (changeListeners == null) {
280                 changeListeners = new ChangeListener[] {listener};
281                 changeSize = 1;
282             } else {
283                 final int oldCapacity = changeListeners.length;
284                 if (locked) {
285                     final int newCapacity = (changeSize < oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1;
286                     changeListeners = Arrays.copyOf(changeListeners, newCapacity);
287                 } else if (changeSize == oldCapacity) {
288                     changeSize = trim(changeSize, changeListeners);
289                     if (changeSize == oldCapacity) {
290                         final int newCapacity = (oldCapacity * 3)/2 + 1;
291                         changeListeners = Arrays.copyOf(changeListeners, newCapacity);
292                     }
293                 }
294                 changeListeners[changeSize++] = listener;
295             }
296             if (changeSize == 1) {

297                 currentValue = observable.getValue();
298             }
299             return this;
300         }
301 
302         @Override
303         protected ExpressionHelper<T> removeListener(ChangeListener<? super T> listener) {
304             if (changeListeners != null) {
305                 for (int index = 0; index < changeSize; index++) {
306                     if (listener.equals(changeListeners[index])) {
307                         if (changeSize == 1) {
308                             if (invalidationSize == 1) {
309                                 return new SingleInvalidation<T>(observable, invalidationListeners[0]);
310                             }
311                             changeListeners = null;
312                             changeSize = 0;
313                         } else if ((changeSize == 2) && (invalidationSize == 0)) {
314                             return new SingleChange<T>(observable, changeListeners[1-index]);
315                         } else {
316                             final int numMoved = changeSize - index - 1;
317                             final ChangeListener<? super T>[] oldListeners = changeListeners;
318                             if (locked) {
319                                 changeListeners = new ChangeListener[changeListeners.length];
320                                 System.arraycopy(oldListeners, 0, changeListeners, 0, index);
321                             }
322                             if (numMoved > 0) {
323                                 System.arraycopy(oldListeners, index+1, changeListeners, index, numMoved);
324                             }
325                             changeSize--;
326                             if (!locked) {
327                                 changeListeners[changeSize] = null; // Let gc do its work
328                             }
329                         }
330                         break;
331                     }
332                 }
333             }
334             return this;
335         }
336 
337         @Override
338         protected void fireValueChangedEvent() {
339             final InvalidationListener[] curInvalidationList = invalidationListeners;
340             final int curInvalidationSize = invalidationSize;
341             final ChangeListener<? super T>[] curChangeList = changeListeners;
342             final int curChangeSize = changeSize;












343 



344             try {
345                 locked = true;
346                 for (int i = 0; i < curInvalidationSize; i++) {
347                     try {
348                         curInvalidationList[i].invalidated(observable);
349                     } catch (Exception e) {
350                         Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
351                     }
352                 }
353                 if (curChangeSize > 0) {
354                     final T oldValue = currentValue;
355                     currentValue = observable.getValue();
356                     final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
357                     if (changed) {
358                         for (int i = 0; i < curChangeSize; i++) {
359                             try {
360                                 curChangeList[i].changed(observable, oldValue, currentValue);
361                             } catch (Exception e) {
362                                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
363                             }
364                         }
365                     }
366                 }
367             } finally {
368                 locked = false;

369             }
370         }
371     }
372 
373 }

  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.binding;
 27 
 28 import java.util.LinkedHashMap;
 29 import java.util.Map;
 30 import java.util.Map.Entry;
 31 
 32 import javafx.beans.InvalidationListener;
 33 import javafx.beans.value.ChangeListener;
 34 import javafx.beans.value.ObservableValue;
 35 


 36 /**
 37  * A convenience class for creating implementations of {@link javafx.beans.value.ObservableValue}.
 38  * It contains all of the infrastructure support for value invalidation- and
 39  * change event notification.
 40  *
 41  * This implementation can handle adding and removing listeners while the
 42  * observers are being notified, but it is not thread-safe.
 43  *
 44  *
 45  */
 46 public abstract class ExpressionHelper<T> extends ExpressionHelperBase {
 47 
 48     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 49     // Static methods
 50 
 51     public static <T> ExpressionHelper<T> addListener(ExpressionHelper<T> helper, ObservableValue<T> observable, InvalidationListener listener) {
 52         if ((observable == null) || (listener == null)) {
 53             throw new NullPointerException();
 54         }
 55         observable.getValue(); // validate observable

173             return (listener.equals(this.listener))? null : this;
174         }
175 
176         @Override
177         protected void fireValueChangedEvent() {
178             final T oldValue = currentValue;
179             currentValue = observable.getValue();
180             final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
181             if (changed) {
182                 try {
183                     listener.changed(observable, oldValue, currentValue);
184                 } catch (Exception e) {
185                     Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
186                 }
187             }
188         }
189     }
190 
191     private static class Generic<T> extends ExpressionHelper<T> {
192 
193         private Map<InvalidationListener, Integer> invalidationListeners = new LinkedHashMap<>();
194         private Map<ChangeListener<? super T>, Integer> changeListeners = new LinkedHashMap<>();



195         private T currentValue;
196         private int weakChangeListenerGcCount = 2;
197         private int weakInvalidationListenerGcCount = 2;
198 
199         private Generic(ObservableValue<T> observable, InvalidationListener listener0, InvalidationListener listener1) {
200             super(observable);
201             this.invalidationListeners.put(listener0, 1);
202             // use merge here in case listener1 == listener0
203             this.invalidationListeners.merge(listener1, 1, Integer::sum);
204         }
205 
206         private Generic(ObservableValue<T> observable, ChangeListener<? super T> listener0, ChangeListener<? super T> listener1) {
207             super(observable);
208             this.changeListeners.put(listener0, 1);
209             // use merge here in case listener1 == listener0
210             this.changeListeners.merge(listener1, 1, Integer::sum);
211             this.currentValue = observable.getValue();
212         }
213 
214         private Generic(ObservableValue<T> observable, InvalidationListener invalidationListener, ChangeListener<? super T> changeListener) {
215             super(observable);
216             this.invalidationListeners.put(invalidationListener, 1);
217             this.changeListeners.put(changeListener, 1);


218             this.currentValue = observable.getValue();
219         }
220 
221         @Override
222         protected Generic<T> addListener(InvalidationListener listener) {
223             if (invalidationListeners.size() == weakInvalidationListenerGcCount) {
224                 removeWeakListeners(invalidationListeners);
225                 if (invalidationListeners.size() == weakInvalidationListenerGcCount) {
226                     weakInvalidationListenerGcCount = (weakInvalidationListenerGcCount * 3)/2 + 1;










227                 }

228             }
229             invalidationListeners.merge(listener, 1, Integer::sum);
230             return this;
231         }
232 
233         @Override
234         protected ExpressionHelper<T> removeListener(InvalidationListener listener) {
235             if (invalidationListeners.containsKey(listener)) {
236                 if (invalidationListeners.merge(listener, -1, Integer::sum) == 0) {
237                     invalidationListeners.remove(listener);
238                     if (invalidationListeners.isEmpty() && changeListeners.size() == 1) {
239                         return new SingleChange<T>(observable, changeListeners.keySet().iterator().next());
240                     } else if ((invalidationListeners.size() == 1) && changeListeners.isEmpty()) {
241                         return new SingleInvalidation<T>(observable, invalidationListeners.keySet().iterator().next());




















242                     }
243                 }
244             }
245             return this;
246         }
247 
248         @Override
249         protected ExpressionHelper<T> addListener(ChangeListener<? super T> listener) {
250             if (changeListeners.size() == weakChangeListenerGcCount) {
251                 removeWeakListeners(changeListeners);
252                 if (changeListeners.size() == weakChangeListenerGcCount) {
253                     weakChangeListenerGcCount = (weakChangeListenerGcCount * 3)/2 + 1;










254                 }

255             }
256             changeListeners.merge(listener, 1, Integer::sum);
257             if (changeListeners.size() == 1) {
258                 currentValue = observable.getValue();
259             }
260             return this;
261         }
262 
263         @Override
264         protected ExpressionHelper<T> removeListener(ChangeListener<? super T> listener) {
265             if (changeListeners.containsKey(listener)) {
266                 if (changeListeners.merge(listener, -1, Integer::sum) == 0) {
267                     changeListeners.remove(listener);
268                     if (changeListeners.isEmpty() && invalidationListeners.size() == 1) {
269                         return new SingleInvalidation<T>(observable, invalidationListeners.keySet().iterator().next());
270                     } else if ((changeListeners.size() == 1) && invalidationListeners.isEmpty()) {
271                         return new SingleChange<T>(observable, changeListeners.keySet().iterator().next());




















272                     }
273                 }
274             }
275             return this;
276         }
277 
278         @Override
279         protected void fireValueChangedEvent() {
280             // Take a copy of listeners to ensure adding and removing listeners
281             // while the observers are being notified is safe
282             final Map<InvalidationListener, Integer> curInvalidationList = new LinkedHashMap<>(invalidationListeners);
283             final Map<ChangeListener<? super T>, Integer> curChangeList = new LinkedHashMap<>(changeListeners);
284 
285             curInvalidationList.entrySet().forEach(entry -> fireInvalidationListeners(entry));
286 
287             if (!curChangeList.isEmpty()) {
288                 final T oldValue = currentValue;
289                 currentValue = observable.getValue();
290                 final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
291                 if (changed) {
292                     curChangeList.entrySet().forEach(entry -> fireChangeListeners(oldValue, entry));
293                 }
294             }
295         }
296 
297         private void fireInvalidationListeners(Entry<InvalidationListener, Integer> entry) {
298             final InvalidationListener listener = entry.getKey();
299             final int registrationCount = entry.getValue();
300             try {
301                 for (int i = 0; i < registrationCount; i++) {
302                     listener.invalidated(observable);





303                 }
304             } catch (Exception e) {
305                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(
306                     Thread.currentThread(), e);
307             }
308         }
309 
310         private void fireChangeListeners(final T oldValue, Entry<ChangeListener<? super T>, Integer> entry) {
311             final ChangeListener<? super T> listener = entry.getKey();
312             final int registrationCount = entry.getValue();
313             try {
314                 for (int i  = 0; i < registrationCount; i++) {
315                     listener.changed(observable, oldValue, currentValue);

316                 }
317             } catch (Exception e) {
318                 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(
319                     Thread.currentThread(), e);
320             }
321         }
322     }

323 }
< prev index next >