Skip to content

Commit

Permalink
#531 add support for maps to modelwrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-mauky committed Dec 7, 2018
1 parent e95813e commit c6c9d0e
Show file tree
Hide file tree
Showing 14 changed files with 954 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright 2018 Manuel Mauky
*
* Licensed 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 de.saxsys.mvvmfx.utils.mapping;

import de.saxsys.mvvmfx.internal.SideEffect;
import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.MapGetter;
import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.MapSetter;
import javafx.beans.property.MapProperty;
import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

/**
* An implementation of {@link PropertyField} that is used when the field of the model class is a {@link Map} and is
* <b>not</b> a JavaFX {@link javafx.beans.property.MapProperty} but is following the old Java-Beans standard, i.e.
* there is getter and setter method for the field.
*
* @param <K> the type of the key.
* @param <V> the type of the value.
*/
class BeanMapPropertyField<M, K, V, T extends ObservableMap<K, V>, R extends Property<T>>
implements PropertyField<T, M, R> {

private final MapGetter<M, K, V> getter;

private final MapSetter<M, K, V> setter;

private Map<K, V> defaultValue;

private final MapProperty<K, V> targetProperty;

BeanMapPropertyField(SideEffect updateFunction, MapGetter<M, K, V> getter, MapSetter<M, K, V> setter,
Supplier<MapProperty<K, V>> propertySupplier) {
this(updateFunction, getter, setter, propertySupplier, Collections.emptyMap());
}

BeanMapPropertyField(SideEffect updateFunction, MapGetter<M, K, V> getter, MapSetter<M, K, V> setter,
Supplier<MapProperty<K, V>> propertySupplier,
Map<K, V> defaultValue) {
this.defaultValue = defaultValue;
this.getter = getter;
this.setter = setter;
this.targetProperty = propertySupplier.get();
this.targetProperty.setValue(FXCollections.observableMap(new HashMap<>()));
this.targetProperty.addListener((MapChangeListener<K, V>) change -> updateFunction.call());
}

static <K, V> void setAll(Map<K, V> target, Map<K, V> newValues) {
target.keySet().retainAll(newValues.keySet());
target.putAll(newValues);
}

@Override
public void commit(M wrappedObject) {
setter.accept(wrappedObject, targetProperty.getValue());
}

@Override
public void reload(M wrappedObject) {
setAll(targetProperty, getter.apply(wrappedObject));
}

@Override
public void resetToDefault() {

targetProperty.get().clear();
setAll(targetProperty, defaultValue);
}

@Override
public void updateDefault(M wrappedObject) {
defaultValue = new HashMap<>(getter.apply(wrappedObject));
}

@Override
public R getProperty() {
return (R) targetProperty;
}

@Override
public boolean isDifferent(M wrappedObject) {
final Map<K, V> modelValue = getter.apply(wrappedObject);
final Map<K, V> wrapperValue = targetProperty;

return !Objects.equals(modelValue, wrapperValue);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package de.saxsys.mvvmfx.utils.mapping;

import de.saxsys.mvvmfx.internal.SideEffect;
import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.MapPropertyAccessor;
import javafx.beans.property.MapProperty;
import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import static de.saxsys.mvvmfx.utils.mapping.BeanMapPropertyField.setAll;

/**
* An implementation of {@link PropertyField} that is used when the field of the model class is a {@link Map} and will
* be mapped to a JavaFX {@link MapProperty}.
*
* @param <K> the type of the key elements.
* @param <V> the type of the value elements.
*/
class FxMapPropertyField<M, K, V, T extends ObservableMap<K, V>, R extends Property<T>>
implements PropertyField<T, M, R> {

private Map<K, V> defaultValue;

private final MapPropertyAccessor<M, K, V> accessor;

private final MapProperty<K, V> targetProperty;

FxMapPropertyField(
SideEffect updateFunction, MapPropertyAccessor<M, K, V> accessor,
Supplier<MapProperty<K, V>> propertySupplier) {
this(updateFunction, accessor, propertySupplier, Collections.emptyMap());
}

FxMapPropertyField(SideEffect updateFunction, MapPropertyAccessor<M, K, V> accessor,
Supplier<MapProperty<K, V>> propertySupplier,
Map<K, V> defaultValue) {
this.accessor = accessor;
this.defaultValue = defaultValue;
this.targetProperty = propertySupplier.get();

this.targetProperty.setValue(FXCollections.observableMap(new HashMap<>()));
this.targetProperty.addListener((MapChangeListener<K, V>) change -> updateFunction.call());
}

@Override
public void commit(M wrappedObject) {
setAll(accessor.apply(wrappedObject), targetProperty.getValue());
}

@Override
public void reload(M wrappedObject) {
setAll(targetProperty, accessor.apply(wrappedObject).getValue());
}

@Override
public void resetToDefault() {
try {
targetProperty.get().clear();
setAll(targetProperty, defaultValue);
} catch (Exception e) {
System.out.println(e);
}
}

@Override
public void updateDefault(M wrappedObject) {
defaultValue = new HashMap<>(accessor.apply(wrappedObject).getValue());
}

@Override
public R getProperty() {
return (R) targetProperty;
}

@Override
public boolean isDifferent(M wrappedObject) {
final Map<K, V> modelValue = accessor.apply(wrappedObject).getValue();
final Map<K, V> wrapperValue = targetProperty;

return !Objects.equals(modelValue, wrapperValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright 2018 Manuel Mauky
*
* Licensed 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 de.saxsys.mvvmfx.utils.mapping;

import de.saxsys.mvvmfx.internal.SideEffect;
import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.MapGetter;
import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.MapImmutableSetter;
import javafx.beans.property.MapProperty;
import javafx.beans.property.Property;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import static de.saxsys.mvvmfx.utils.mapping.BeanMapPropertyField.setAll;

class ImmutableMapPropertyField<M, K, V, T extends ObservableMap<K, V>, R extends Property<T>>
implements ImmutablePropertyField<T, M, R> {

private final MapGetter<M, K, V> getter;

private final MapImmutableSetter<M, K, V> immutableSetter;

private Map<K, V> defaultValue;

private final MapProperty<K, V> targetProperty;

ImmutableMapPropertyField(
SideEffect updateFunction,
MapGetter<M, K, V> getter, MapImmutableSetter<M, K, V> immutableSetter,
Supplier<MapProperty<K, V>> propertySupplier) {
this(updateFunction, getter, immutableSetter, propertySupplier, Collections.emptyMap());
}

ImmutableMapPropertyField(SideEffect updateFunction,
MapGetter<M, K, V> getter, MapImmutableSetter<M, K, V> immutableSetter,
Supplier<MapProperty<K, V>> propertySupplier, Map<K, V> defaultValue) {
this.defaultValue = defaultValue;
this.getter = getter;
this.immutableSetter = immutableSetter;
this.targetProperty = propertySupplier.get();
this.targetProperty.setValue(FXCollections.observableMap(new HashMap<>()));
this.targetProperty.addListener((MapChangeListener<K, V>) change -> updateFunction.call());
}

@Override
public void commit(M wrappedObject) {
// commit is not supported because the model instance is immutable.
}

@Override
public M commitImmutable(M wrappedObject) {
return immutableSetter.apply(wrappedObject, targetProperty.getValue());
}

@Override
public void reload(M wrappedObject) {
setAll(targetProperty, getter.apply(wrappedObject));
}

@Override
public void resetToDefault() {
setAll(targetProperty, defaultValue);
}

@Override
public void updateDefault(M wrappedObject) {
defaultValue = new HashMap<>(getter.apply(wrappedObject));
}

@Override
public R getProperty() {
return (R) targetProperty;
}

@Override
public boolean isDifferent(M wrappedObject) {
final Map<K, V> modelValue = getter.apply(wrappedObject);
final Map<K, V> wrappedValue = targetProperty;

return !Objects.equals(modelValue, wrappedValue);
}
}
Loading

0 comments on commit c6c9d0e

Please sign in to comment.