This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
commit b3bb21f0d45e4d636f62ac450b53635764602cc9 Author: Andi Huber <[email protected]> AuthorDate: Thu Aug 13 12:16:43 2020 +0200 ISIS-2340: fx: refactor binding converter for reuse --- .../javafx/model/binding/BindingConverter.java | 43 +++++ .../viewer/javafx/model/binding/BindingsFx.java} | 134 ++++---------- .../ui/components/text/TextFieldFactory.java | 204 ++------------------- 3 files changed, 86 insertions(+), 295 deletions(-) diff --git a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingConverter.java b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingConverter.java new file mode 100644 index 0000000..d63b867 --- /dev/null +++ b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingConverter.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.isis.incubator.viewer.javafx.model.binding; + +import org.apache.isis.core.commons.internal.base._Casts; +import org.apache.isis.core.metamodel.spec.ManagedObject; +import org.apache.isis.core.metamodel.spec.ManagedObjects; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; + +public interface BindingConverter<T> { + + ObjectSpecification getValueSpecification(); + + default T unwrap(ManagedObject object) { + return _Casts.uncheckedCast(ManagedObjects.UnwrapUtil.single(object)); + } + + default ManagedObject wrap(T pojo) { + return ManagedObject.of(getValueSpecification(), pojo); + } + + +} + + + + diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java similarity index 66% copy from incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java copy to incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java index 50ffb4b..0fd6c61 100644 --- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java +++ b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/binding/BindingsFx.java @@ -16,108 +16,57 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.isis.incubator.viewer.javafx.ui.components.text; +package org.apache.isis.incubator.viewer.javafx.model.binding; import java.lang.ref.WeakReference; import java.util.Objects; -import javax.inject.Inject; - -import org.springframework.core.annotation.Order; - -import org.apache.isis.applib.annotation.OrderPrecedence; import org.apache.isis.core.commons.binding.Bindable; import org.apache.isis.core.commons.binding.ChangeListener; import org.apache.isis.core.commons.binding.Observable; -import org.apache.isis.core.commons.internal.base._Casts; import org.apache.isis.core.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.facets.value.string.StringValueFacet; -import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter; -import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty; import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.ManagedObjects; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx; -import org.apache.isis.viewer.common.model.binding.UiComponentFactory.ComponentRequest; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.val; import javafx.beans.property.Property; import javafx.beans.value.ObservableValue; -import javafx.scene.Node; -import javafx.scene.control.TextField; +import lombok.NonNull; +import lombok.val; +import lombok.experimental.UtilityClass; [email protected] -@Order(OrderPrecedence.MIDPOINT) -@RequiredArgsConstructor(onConstructor_ = {@Inject}) -public class TextFieldFactory implements UiComponentHandlerFx { +@UtilityClass +public class BindingsFx { + + public static <L, R> void bind( + final @NonNull Property<L> leftProperty, + final @NonNull Observable<ManagedObject> rightObservable, + final @NonNull BindingConverter<L> converter) { - @Override - public boolean isHandling(ComponentRequest request) { - return request.hasFeatureTypeFacet(StringValueFacet.class); + leftProperty.setValue(converter.unwrap(rightObservable.getValue())); + rightObservable.addListener((e,o,n)->{ + leftProperty.setValue(converter.unwrap(n)); + }); } - @Override - public Node handle(ComponentRequest request) { - - val uiComponent = new TextField(); - val valueSpec = request.getFeatureTypeSpec(); - val converter = Converter.<String>of(valueSpec); - - if(request.getManagedFeature() instanceof ManagedParameter) { - - val managedParameter = (ManagedParameter)request.getManagedFeature(); - - bindBidirectional( - uiComponent.textProperty(), - managedParameter.getValue(), - converter); - - //TODO bind parameter validation feedback - - } else if(request.getManagedFeature() instanceof ManagedProperty) { - - val managedProperty = (ManagedProperty)request.getManagedFeature(); - - // readonly binding - bind( - uiComponent.textProperty(), - managedProperty.getValue(), - converter); - - //TODO allow property editing - //TODO bind property validation feedback - } - - return uiComponent; + public static <L, R> void bindBidirectional( + final @NonNull Property<L> leftProperty, + final @NonNull Bindable<ManagedObject> rightProperty, + final @NonNull BindingConverter<L> converter) { + final InternalBidirBinding<L> binding = new InternalBidirBinding<L>(leftProperty, rightProperty, converter); + leftProperty.setValue(converter.unwrap(rightProperty.getValue())); + leftProperty.addListener(binding); + rightProperty.addListener(binding); } - - // -- TODO these binding helpers need to move (for reuse) ... - @RequiredArgsConstructor(staticName = "of") - private static final class Converter<T> { - - private final ObjectSpecification valueSpec; - - public T unwrap(ManagedObject object) { - return _Casts.uncheckedCast(ManagedObjects.UnwrapUtil.single(object)); - } - - public ManagedObject wrap(T pojo) { - return ManagedObject.of(valueSpec, pojo); - } - } + // -- INTERNAL - private static class BidirBinding<T> + private static class InternalBidirBinding<T> implements javafx.beans.value.ChangeListener<T>, ChangeListener<ManagedObject>{ private final WeakReference<Property<T>> leftRef; private final WeakReference<Bindable<ManagedObject>> rightRef; - private final Converter<T> converter; + private final BindingConverter<T> converter; private boolean updating = false; private final int cachedHash; @@ -130,10 +79,10 @@ public class TextFieldFactory implements UiComponentHandlerFx { } - public BidirBinding( + public InternalBidirBinding( final @NonNull Property<T> left, final @NonNull Bindable<ManagedObject> right, - final @NonNull Converter<T> converter) { + final @NonNull BindingConverter<T> converter) { this.leftRef = new WeakReference<>(left); this.rightRef = new WeakReference<>(right); @@ -166,8 +115,8 @@ public class TextFieldFactory implements UiComponentHandlerFx { return false; } - if (obj instanceof BidirBinding) { - final BidirBinding<?> otherBinding = (BidirBinding<?>) obj; + if (obj instanceof InternalBidirBinding) { + final InternalBidirBinding<?> otherBinding = (InternalBidirBinding<?>) obj; final Object otherLeft = otherBinding.getLeft(); final Object otherRight = otherBinding.getRight(); if ((otherLeft == null) || (otherRight == null)) { @@ -251,26 +200,5 @@ public class TextFieldFactory implements UiComponentHandlerFx { } } - - public static <L, R> void bind( - final @NonNull Property<L> leftProperty, - final @NonNull Observable<ManagedObject> rightObservable, - final @NonNull Converter<L> converter) { - - leftProperty.setValue(converter.unwrap(rightObservable.getValue())); - rightObservable.addListener((e,o,n)->{ - leftProperty.setValue(converter.unwrap(n)); - }); - } - - public static <L, R> void bindBidirectional( - final @NonNull Property<L> leftProperty, - final @NonNull Bindable<ManagedObject> rightProperty, - final @NonNull Converter<L> converter) { - final BidirBinding<L> binding = new BidirBinding<L>(leftProperty, rightProperty, converter); - leftProperty.setValue(converter.unwrap(rightProperty.getValue())); - leftProperty.addListener(binding); - rightProperty.addListener(binding); - } - + } diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java index 50ffb4b..9fc6cad 100644 --- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java +++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/text/TextFieldFactory.java @@ -18,36 +18,25 @@ */ package org.apache.isis.incubator.viewer.javafx.ui.components.text; -import java.lang.ref.WeakReference; -import java.util.Objects; - import javax.inject.Inject; import org.springframework.core.annotation.Order; import org.apache.isis.applib.annotation.OrderPrecedence; -import org.apache.isis.core.commons.binding.Bindable; -import org.apache.isis.core.commons.binding.ChangeListener; -import org.apache.isis.core.commons.binding.Observable; -import org.apache.isis.core.commons.internal.base._Casts; -import org.apache.isis.core.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.facets.value.string.StringValueFacet; import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter; import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty; -import org.apache.isis.core.metamodel.spec.ManagedObject; -import org.apache.isis.core.metamodel.spec.ManagedObjects; import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.incubator.viewer.javafx.model.binding.BindingConverter; +import org.apache.isis.incubator.viewer.javafx.model.binding.BindingsFx; import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx; import org.apache.isis.viewer.common.model.binding.UiComponentFactory.ComponentRequest; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.val; - -import javafx.beans.property.Property; -import javafx.beans.value.ObservableValue; import javafx.scene.Node; import javafx.scene.control.TextField; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.val; @org.springframework.stereotype.Component @Order(OrderPrecedence.MIDPOINT) @@ -64,13 +53,13 @@ public class TextFieldFactory implements UiComponentHandlerFx { val uiComponent = new TextField(); val valueSpec = request.getFeatureTypeSpec(); - val converter = Converter.<String>of(valueSpec); + val converter = StringConverter.of(valueSpec); if(request.getManagedFeature() instanceof ManagedParameter) { val managedParameter = (ManagedParameter)request.getManagedFeature(); - bindBidirectional( + BindingsFx.bindBidirectional( uiComponent.textProperty(), managedParameter.getValue(), converter); @@ -82,7 +71,7 @@ public class TextFieldFactory implements UiComponentHandlerFx { val managedProperty = (ManagedProperty)request.getManagedFeature(); // readonly binding - bind( + BindingsFx.bind( uiComponent.textProperty(), managedProperty.getValue(), converter); @@ -94,183 +83,14 @@ public class TextFieldFactory implements UiComponentHandlerFx { return uiComponent; } - // -- TODO these binding helpers need to move (for reuse) ... + // -- HELPER @RequiredArgsConstructor(staticName = "of") - private static final class Converter<T> { - - private final ObjectSpecification valueSpec; - - public T unwrap(ManagedObject object) { - return _Casts.uncheckedCast(ManagedObjects.UnwrapUtil.single(object)); - } + private static final class StringConverter implements BindingConverter<String> { - public ManagedObject wrap(T pojo) { - return ManagedObject.of(valueSpec, pojo); - } + @Getter(onMethod_ = {@Override}) + private final ObjectSpecification valueSpecification; } - - private static class BidirBinding<T> - implements - javafx.beans.value.ChangeListener<T>, - ChangeListener<ManagedObject>{ - - private final WeakReference<Property<T>> leftRef; - private final WeakReference<Bindable<ManagedObject>> rightRef; - private final Converter<T> converter; - private boolean updating = false; - private final int cachedHash; - - private Property<T> getLeft() { - return leftRef.get(); - } - - private Bindable<ManagedObject> getRight() { - return rightRef.get(); - } - - - public BidirBinding( - final @NonNull Property<T> left, - final @NonNull Bindable<ManagedObject> right, - final @NonNull Converter<T> converter) { - - this.leftRef = new WeakReference<>(left); - this.rightRef = new WeakReference<>(right); - this.converter = converter; - cachedHash = Objects.hash(left, right, converter); - } - @Override - public void changed(ObservableValue<? extends T> leftObservable, T oldValue, T newValue) { - changed(oldValue, newValue, null, null); - } - - @Override - public void changed( - Observable<? extends ManagedObject> rightObservable, - ManagedObject oldValue, - ManagedObject newValue) { - changed(null, null, oldValue, newValue); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - final Object left = getLeft(); - final Object right = getRight(); - if ((left == null) || (right == null)) { - return false; - } - - if (obj instanceof BidirBinding) { - final BidirBinding<?> otherBinding = (BidirBinding<?>) obj; - final Object otherLeft = otherBinding.getLeft(); - final Object otherRight = otherBinding.getRight(); - if ((otherLeft == null) || (otherRight == null)) { - return false; - } - - if (left == otherLeft && right == otherRight) { - return true; - } - if (left == otherRight && right == otherLeft) { - return true; - } - } - return false; - } - - @Override - public int hashCode() { - return cachedHash; - } - - // -- HELPER - - private void changed(T oldPojo, T newPojo, ManagedObject oldValue, ManagedObject newValue) { - if (updating) { - return; - } - val left = getLeft(); - val right = getRight(); - if(!isStillBound(left, right)) { - return; - } - - try { - updating = true; - if(newValue!=null) { - left.setValue(converter.unwrap(newValue)); - } else { - right.setValue(converter.wrap(newPojo)); - } - } catch (RuntimeException e) { - try { - if(newValue!=null) { - left.setValue(converter.unwrap(oldValue)); - } else { - right.setValue(converter.wrap(oldPojo)); - } - } catch (Exception e2) { - e2.addSuppressed(e); - left.removeListener(this); - right.removeListener(this); - throw _Exceptions.unrecoverableFormatted( - "Bidirectional binding failed with an attempt to restore the " - + "Observable to the previous value. " - + "Removing the bidirectional binding from bindables %s and %s", - ""+left, - ""+right, - e2); - } - throw _Exceptions.unrecoverable( - "Bidirectional binding failed, setting to the previous value", e); - } finally { - updating = false; - } - } - - private boolean isStillBound( - final Property<T> left, - final Bindable<ManagedObject> right) { - - if ((left == null) || (right == null)) { - if (left != null) { - left.removeListener(this); - } - if (right != null) { - right.removeListener(this); - } - return false; - } - return true; - } - - } - - public static <L, R> void bind( - final @NonNull Property<L> leftProperty, - final @NonNull Observable<ManagedObject> rightObservable, - final @NonNull Converter<L> converter) { - - leftProperty.setValue(converter.unwrap(rightObservable.getValue())); - rightObservable.addListener((e,o,n)->{ - leftProperty.setValue(converter.unwrap(n)); - }); - } - - public static <L, R> void bindBidirectional( - final @NonNull Property<L> leftProperty, - final @NonNull Bindable<ManagedObject> rightProperty, - final @NonNull Converter<L> converter) { - final BidirBinding<L> binding = new BidirBinding<L>(leftProperty, rightProperty, converter); - leftProperty.setValue(converter.unwrap(rightProperty.getValue())); - leftProperty.addListener(binding); - rightProperty.addListener(binding); - } }
