From 29456341e59d95ce71b86f3d83df386679cfcc2a Mon Sep 17 00:00:00 2001 From: Hendrik Ebbers Date: Thu, 23 Feb 2017 23:58:16 +0100 Subject: [PATCH] generic binding types for fluent API --- .../javax/observer/ValueWillChangeEvent.java | 4 +- .../binding/BidirectionalBindable.java | 2 +- .../java/javax/observer/binding/Bindable.java | 6 +- .../ConvertableBidirectionalBindable.java | 2 +- .../observer/binding/ConvertableBindable.java | 2 +- .../java/com/guigarage/binding/Binding.java | 90 ++++---- .../com/guigarage/binding/SwingBinding.java | 196 +++++++++--------- 7 files changed, 157 insertions(+), 145 deletions(-) diff --git a/api/src/main/java/javax/observer/ValueWillChangeEvent.java b/api/src/main/java/javax/observer/ValueWillChangeEvent.java index 4548c2d..60b1957 100644 --- a/api/src/main/java/javax/observer/ValueWillChangeEvent.java +++ b/api/src/main/java/javax/observer/ValueWillChangeEvent.java @@ -2,9 +2,7 @@ import java.util.Optional; -/** - * Created by hendrikebbers on 30.01.17. - */ + public interface ValueWillChangeEvent { Observable getObservable(); diff --git a/api/src/main/java/javax/observer/binding/BidirectionalBindable.java b/api/src/main/java/javax/observer/binding/BidirectionalBindable.java index e5eaeca..0eb6f83 100644 --- a/api/src/main/java/javax/observer/binding/BidirectionalBindable.java +++ b/api/src/main/java/javax/observer/binding/BidirectionalBindable.java @@ -10,7 +10,7 @@ * TODO: should this extend {@link Property}??? * @param value type */ -public interface BidirectionalBindable extends Bindable { +public interface BidirectionalBindable> extends Bindable { /** * Creates an bidirectional binding by binding this Bindable to the given {@link Property}. diff --git a/api/src/main/java/javax/observer/binding/Bindable.java b/api/src/main/java/javax/observer/binding/Bindable.java index e2d4a0a..330ad3d 100644 --- a/api/src/main/java/javax/observer/binding/Bindable.java +++ b/api/src/main/java/javax/observer/binding/Bindable.java @@ -8,11 +8,9 @@ * This interface defines objects that can be bound to an {@link Observable}. By doing so any change of * the value of the {@link Observable} will be propagated to this object. * - * TODO: should this extend {@link Observable}??? - * * @param type of the value */ -public interface Bindable { +public interface Bindable> { /** * Creates an unidirectional binding by binding this Bindable to the given {@link Observable}. @@ -28,6 +26,6 @@ public interface Bindable { * @param handler the error handler * @return this object. This can be used to create a small fluent API */ - ConvertableBindable withErrorHandler(Consumer handler); + I withErrorHandler(Consumer handler); } diff --git a/api/src/main/java/javax/observer/binding/ConvertableBidirectionalBindable.java b/api/src/main/java/javax/observer/binding/ConvertableBidirectionalBindable.java index fe9fcc4..bef8c77 100644 --- a/api/src/main/java/javax/observer/binding/ConvertableBidirectionalBindable.java +++ b/api/src/main/java/javax/observer/binding/ConvertableBidirectionalBindable.java @@ -9,7 +9,7 @@ * offers bindings between different value types by defining {@link Function} based converters. * @param value type */ -public interface ConvertableBidirectionalBindable extends ConvertableBindable, BidirectionalBindable { +public interface ConvertableBidirectionalBindable> extends ConvertableBindable, BidirectionalBindable { /** *By defining a converter a bidirectional binding to an {@link Property} with a different value diff --git a/api/src/main/java/javax/observer/binding/ConvertableBindable.java b/api/src/main/java/javax/observer/binding/ConvertableBindable.java index 7a8909a..effd9f8 100644 --- a/api/src/main/java/javax/observer/binding/ConvertableBindable.java +++ b/api/src/main/java/javax/observer/binding/ConvertableBindable.java @@ -9,7 +9,7 @@ * offers bindings between different value types by defining {@link Function} based converters. * @param value type */ -public interface ConvertableBindable extends Bindable { +public interface ConvertableBindable> extends Bindable { /** * By defining a converter a unidirectional binding to an {@link Observable} with a different value diff --git a/impl/src/main/java/com/guigarage/binding/Binding.java b/impl/src/main/java/com/guigarage/binding/Binding.java index 2de13aa..44790e8 100644 --- a/impl/src/main/java/com/guigarage/binding/Binding.java +++ b/impl/src/main/java/com/guigarage/binding/Binding.java @@ -4,7 +4,6 @@ import javax.observer.Property; import javax.observer.Subscription; import javax.observer.binding.ConvertableBidirectionalBindable; -import javax.observer.binding.ConvertableBindable; import java.util.function.Consumer; import java.util.function.Function; @@ -14,49 +13,56 @@ */ public class Binding { - public static ConvertableBidirectionalBindable bind(Property property) { - return new ConvertableBidirectionalBindable() { - - private Consumer errorHandler = e -> e.printStackTrace(); - - private boolean bindingCalled = false; - - @Override - public Subscription bidirectionalTo(Property toProperty, Function converter, Function converter2) { - Subscription subscription1 = to(toProperty, converter); - Subscription subscription2 = bind(property, toProperty, converter2); - return () -> { - subscription1.unsubscribe(); - subscription2.unsubscribe(); - }; - } - - @Override - public Subscription to(Observable observable, Function converter) { - return bind(observable, property, converter); - } - - private Subscription bind(Observable observable, Property property, Function converter) { - return observable.onChanged(e -> { - if(!bindingCalled) { - bindingCalled = true; - try { - property.setValue(converter.apply(e.getValue())); - } catch (Exception ex) { - errorHandler.accept(ex); - } finally { - bindingCalled = false; - } + private static class InternalConvertableBidirectionalBindable implements ConvertableBidirectionalBindable> { + + private final Property property; + + private Consumer errorHandler = e -> e.printStackTrace(); + + private boolean bindingCalled = false; + + public InternalConvertableBidirectionalBindable(Property property) { + this.property = property; + } + + @Override + public Subscription bidirectionalTo(Property toProperty, Function converter, Function converter2) { + Subscription subscription1 = to(toProperty, converter); + Subscription subscription2 = bind(property, toProperty, converter2); + return () -> { + subscription1.unsubscribe(); + subscription2.unsubscribe(); + }; + } + + @Override + public Subscription to(Observable observable, Function converter) { + return bind(observable, property, converter); + } + + private Subscription bind(Observable observable, Property property, Function converter) { + return observable.onChanged(e -> { + if (!bindingCalled) { + bindingCalled = true; + try { + property.setValue(converter.apply(e.getValue())); + } catch (Exception ex) { + errorHandler.accept(ex); + } finally { + bindingCalled = false; } - }); - } + } + }); + } - @Override - public ConvertableBindable withErrorHandler(Consumer handler) { - this.errorHandler = errorHandler; - return this; - } + @Override + public InternalConvertableBidirectionalBindable withErrorHandler(Consumer handler) { + this.errorHandler = errorHandler; + return this; + } + } - }; + public static ConvertableBidirectionalBindable bind(Property property) { + return new InternalConvertableBidirectionalBindable(property); } } diff --git a/impl/src/main/java/com/guigarage/binding/SwingBinding.java b/impl/src/main/java/com/guigarage/binding/SwingBinding.java index d0851a4..59a1af0 100644 --- a/impl/src/main/java/com/guigarage/binding/SwingBinding.java +++ b/impl/src/main/java/com/guigarage/binding/SwingBinding.java @@ -4,7 +4,6 @@ import javax.observer.Property; import javax.observer.Subscription; import javax.observer.binding.ConvertableBidirectionalBindable; -import javax.observer.binding.ConvertableBindable; import javax.swing.SwingUtilities; import java.awt.Component; import java.beans.PropertyChangeListener; @@ -27,114 +26,125 @@ public class SwingBinding { private static Executor backgroundExecutor = Executors.newSingleThreadExecutor(); - public static ConvertableBidirectionalBindable bind(final Component component, final String attribute) { - return new ConvertableBidirectionalBindable() { + private static class InternalConvertableBidirectionalBindable implements ConvertableBidirectionalBindable> { - private AtomicBoolean bindingCalled = new AtomicBoolean(false); + private AtomicBoolean bindingCalled = new AtomicBoolean(false); - private Consumer errorHandler = e -> e.printStackTrace(); + private Consumer errorHandler = e -> e.printStackTrace(); - private Lock bindingLock = new ReentrantLock(); + private Lock bindingLock = new ReentrantLock(); - private U callLocked(Supplier supplier) { - bindingLock.lock(); - try { - return supplier.get(); - } finally { - bindingLock.unlock(); - } - } + private final Component component; - private void callLocked(Runnable runnable) { - callLocked(() -> { - runnable.run(); - return null; - }); - } + private final String attribute; - @Override - public Subscription bidirectionalTo(Property property, Function converter, Function converter2) { - try { - final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(attribute, component.getClass()); - final PropertyEditor propertyEditor = propertyDescriptor.createPropertyEditor(component); - Subscription subscription = bind(property, converter, propertyEditor); - - PropertyChangeListener listener = e -> { - if (!bindingCalled.get()) { - bindingCalled.set(true); - try { - backgroundExecutor.execute(() -> { - callLocked(() -> { - try { - property.setValue(converter2.apply((T) e.getNewValue())); - } catch (Exception ex) { - SwingUtilities.invokeLater(() -> { - errorHandler.accept(ex); - }); - } - }); - }); - } catch (Exception e1) { - errorHandler.accept(e1); - } finally { - bindingCalled.set(false); - } - } - }; + public InternalConvertableBidirectionalBindable(Component component, String attribute) { + this.component = component; + this.attribute = attribute; + } - component.addPropertyChangeListener(listener); - - return () -> { - component.removePropertyChangeListener(listener); - subscription.unsubscribe(); - }; - } catch (Exception e1) { - throw new RuntimeException("Can not bind!", e1); - } - } - - @Override - public Subscription to(Observable observable, Function converter) { - try { - final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(attribute, component.getClass()); - final PropertyEditor propertyEditor = propertyDescriptor.createPropertyEditor(component); - return bind(observable, converter, propertyEditor); - } catch (Exception e1) { - throw new RuntimeException("Can not bind!", e1); - } + private U callLocked(Supplier supplier) { + bindingLock.lock(); + try { + return supplier.get(); + } finally { + bindingLock.unlock(); } - - private Subscription bind(Observable observable, Function converter, PropertyEditor propertyEditor) { - return observable.onChanged(e -> { - callLocked(() -> { + } + + private void callLocked(Runnable runnable) { + callLocked(() -> { + runnable.run(); + return null; + }); + } + + @Override + public Subscription bidirectionalTo(Property property, Function converter, Function converter2) { + try { + final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(attribute, component.getClass()); + final PropertyEditor propertyEditor = propertyDescriptor.createPropertyEditor(component); + Subscription subscription = bind(property, converter, propertyEditor); + + PropertyChangeListener listener = e -> { + if (!bindingCalled.get()) { + bindingCalled.set(true); try { - SwingUtilities.invokeAndWait(() -> { - if (!bindingCalled.get()) { - bindingCalled.set(true); + backgroundExecutor.execute(() -> { + callLocked(() -> { try { - propertyEditor.setValue(converter.apply(e.getValue())); - } catch (Exception e1) { - errorHandler.accept(e1); - } finally { - bindingCalled.set(false); + property.setValue(converter2.apply((T) e.getNewValue())); + } catch (Exception ex) { + SwingUtilities.invokeLater(() -> { + errorHandler.accept(ex); + }); } - } + }); }); } catch (Exception e1) { - SwingUtilities.invokeLater(() -> { - errorHandler.accept(e1); - }); + errorHandler.accept(e1); + } finally { + bindingCalled.set(false); } - }); - }); - } + } + }; + + component.addPropertyChangeListener(listener); - @Override - public ConvertableBindable withErrorHandler(Consumer handler) { - this.errorHandler = handler; - return this; + return () -> { + component.removePropertyChangeListener(listener); + subscription.unsubscribe(); + }; + } catch (Exception e1) { + throw new RuntimeException("Can not bind!", e1); } + } + + @Override + public Subscription to(Observable observable, Function converter) { + try { + final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(attribute, component.getClass()); + final PropertyEditor propertyEditor = propertyDescriptor.createPropertyEditor(component); + return bind(observable, converter, propertyEditor); + } catch (Exception e1) { + throw new RuntimeException("Can not bind!", e1); + } + } + + private Subscription bind(Observable observable, Function converter, PropertyEditor propertyEditor) { + return observable.onChanged(e -> { + callLocked(() -> { + try { + SwingUtilities.invokeAndWait(() -> { + if (!bindingCalled.get()) { + bindingCalled.set(true); + try { + propertyEditor.setValue(converter.apply(e.getValue())); + } catch (Exception e1) { + errorHandler.accept(e1); + } finally { + bindingCalled.set(false); + } + } + }); + } catch (Exception e1) { + SwingUtilities.invokeLater(() -> { + errorHandler.accept(e1); + }); + } + }); + }); + } + + @Override + public InternalConvertableBidirectionalBindable withErrorHandler(Consumer handler) { + this.errorHandler = handler; + return this; + } + + } - }; + public static ConvertableBidirectionalBindable bind(final Component component, final String attribute) { + return new InternalConvertableBidirectionalBindable(component, attribute); } }