Skip to content

Commit

Permalink
Add support for MapProperty #99
Browse files Browse the repository at this point in the history
  • Loading branch information
amischler committed Apr 25, 2015
1 parent 2553e9c commit b8b44ba
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 75 deletions.
Expand Up @@ -55,6 +55,9 @@ public PropertyEditorController(AbstractFXForm fxForm, Element element) {
protected void bind(final FXFormNode fxFormNode) {
viewChangeListener = new ChangeListener() {
public void changed(ObservableValue observableValue, Object o, Object o1) {
if ((o == null && o1 == null) || (o != null && o.equals(o1))) {
return;
}
try {
Adapter adapter = annotationAdapterProvider.getAdapter(getElement().getType(), getNode().getProperty().getClass(), getElement(), getNode());
if (adapter == null) {
Expand Down
Expand Up @@ -6,6 +6,7 @@
import com.dooapp.fxform.view.skin.InlineSkin;
import javafx.beans.property.MapProperty;
import javafx.beans.property.SimpleMapProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;

import javax.validation.ConstraintViolation;
Expand All @@ -20,6 +21,8 @@
*/
public class MapEditorControl<T> extends AbstractFXForm {

private MapElementProvider mapElementProvider;

public MapEditorControl() {
super();
// no validation can be performed
Expand All @@ -43,10 +46,11 @@ public MessageInterpolator getMessageInterpolator() {
setSkin(new InlineSkin(this));
}

private final MapProperty<String, T> map = new SimpleMapProperty<>();
private final MapProperty<String, T> map = new SimpleMapProperty<>(FXCollections.observableHashMap());

public void setValueType(Class<T> valueType) {
elementsProperty().bind(new MapElementBinding(map, valueType));
mapElementProvider = new MapElementProvider(map, valueType);
elementsProperty().bind(mapElementProvider.elementsProperty());
}

public ObservableMap<String, T> getMap() {
Expand All @@ -60,4 +64,10 @@ public MapProperty<String, T> mapProperty() {
public void setMap(ObservableMap<String, T> map) {
this.map.set(map);
}

public void dispose() {
elementsProperty().unbind();
elementsProperty().clear();
mapElementProvider.dispose();
}
}

This file was deleted.

@@ -0,0 +1,73 @@
package com.dooapp.fxform.view.control.map;

import com.dooapp.fxform.model.Element;
import javafx.beans.property.ListProperty;
import javafx.beans.property.MapProperty;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;

import java.util.stream.Collectors;

/**
* User: Antoine Mischler <antoine@dooapp.com>
* Date: 24/04/15
* Time: 14:52
*/
public class MapElementProvider<K, V> {

private final ListProperty<Element<V>> elements = new SimpleListProperty<>(FXCollections.observableArrayList());

private final MapProperty<K, V> map;

private final Class<V> valueType;
private final MapChangeListener<K, V> mapChangeListener;

public MapElementProvider(MapProperty<K, V> map, Class<V> valueType) {
this.map = map;
this.valueType = valueType;
mapChangeListener = change -> {
if (change.wasAdded()) {
if (getElementByKey(change.getKey()) == null) {
elements.add(new MapEntryPropertyElement(map, change.getKey(), valueType));
}
} else if (change.wasRemoved()) {
elements.remove(getElementByKey(change.getKey()));
}
};
map.addListener(mapChangeListener);
init();
}

private Element getElementByKey(K key) {
Element toRemove = null;
for (Element element : elements) {
if (((MapEntryElement) element).getKey().equals(key)) {
toRemove = element;
break;
}
}
return toRemove;
}

protected void init() {
elements.addAll(map.keySet().stream()
.map(key -> new MapEntryPropertyElement<>(map, key, valueType))
.collect(Collectors.toList()));
}

public ObservableList<Element<V>> getElements() {
return elements.get();
}

public ReadOnlyListProperty<Element<V>> elementsProperty() {
return elements;
}

public void dispose() {
map.removeListener(mapChangeListener);
}

}
Expand Up @@ -3,7 +3,10 @@
import com.dooapp.fxform.model.Element;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.*;
import javafx.beans.property.MapProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.MapChangeListener;

Expand All @@ -29,16 +32,6 @@ public void onChanged(Change<? extends K, ? extends V> change) {
}
}

protected class EntryMapInvalidationListener implements InvalidationListener {

@Override
public void invalidated(Observable observable) {
for (InvalidationListener invalidationListener : invalidationListeners) {
invalidationListener.invalidated(MapEntryElement.this);
}
}
}

protected final MapProperty<K, V> map;

protected final K key;
Expand All @@ -51,18 +44,53 @@ public void invalidated(Observable observable) {

private final List<InvalidationListener> invalidationListeners = new ArrayList<>();

private final EntryMapChangeListener changeListener;
private final MapChangeListener<K, V> changeListener = new MapChangeListener<K, V>() {
@Override
public void onChanged(Change<? extends K, ? extends V> change) {
if (key.equals(change.getKey())) {
for (ChangeListener changeListener : changeListeners) {
changeListener.changed(MapEntryElement.this, null, map.get(key));
}
}
}
};

private final InvalidationListener invalidationListener = new InvalidationListener() {

private final EntryMapInvalidationListener invalidationListener;
@Override
public void invalidated(Observable observable) {
for (InvalidationListener invalidationListener : invalidationListeners) {
invalidationListener.invalidated(MapEntryElement.this);
}
}
};

public MapEntryElement(MapProperty<K, V> map, K key, Class<V> valueType) {
this.map = map;
this.key = key;
this.valueType = valueType;
changeListener = new EntryMapChangeListener();
map.addListener(changeListener);
invalidationListener = new EntryMapInvalidationListener();
map.addListener(invalidationListener);
map.addListener(new MapChangeListener<K, V>() {
@Override
public void onChanged(Change<? extends K, ? extends V> change) {
if (key.equals(change.getKey())) {
for (ChangeListener changeListener : changeListeners) {
changeListener.changed(MapEntryElement.this, null, map.get(key));
}
}
}
});
map.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
for (InvalidationListener invalidationListener : invalidationListeners) {
invalidationListener.invalidated(MapEntryElement.this);
}
}
});
}

public K getKey() {
return key;
}

@Override
Expand Down Expand Up @@ -103,7 +131,7 @@ public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
@Override
public void dispose() {
map.removeListener(changeListener);
map.removeListener(invalidationListener);
//map.removeListener(invalidationListener);
}

@Override
Expand Down
Expand Up @@ -54,7 +54,7 @@ public Node getNode() {

@Override
public void dispose() {

mapEditorControl.dispose();
}
};
}
Expand Down
15 changes: 12 additions & 3 deletions demo/src/main/java/com/dooapp/fxform/MyBean.java
Expand Up @@ -82,9 +82,18 @@ protected MyBean(String name, String email, String message, boolean subscribe, S
this.message.set(message);
this.subscribe.set(subscribe);
this.subject.set(subject);
this.list.addAll(new TableBean("Name 1", 99), new TableBean("Name 2", 98));
this.map.put("a", "1");
this.map.put("b", "2");
this.list.addAll(new TableBean("Name 1", 99),
new TableBean("Name 2", 98),
new TableBean("Name 3", 97));
this.map.put("John", "john@fxform2.com");
this.map.put("Jane", "jane@fxform2.com");
this.map.put("Janis", "janis@fxform2.com");
/**this.map.addListener(new MapChangeListener<String, String>() {
@Override
public void onChanged(Change<? extends String, ? extends String> change) {
System.out.println(change.toString());
}
}); */
((StringProperty) welcome).bind(this.name.concat(", welcome!"));
((BooleanProperty) unsubscribe).bind(this.subscribe.not());
}
Expand Down
42 changes: 31 additions & 11 deletions demo/src/main/java/com/dooapp/fxform/ObjectPropertyObserver.java
Expand Up @@ -13,8 +13,12 @@
package com.dooapp.fxform;

import com.dooapp.fxform.reflection.impl.ReflectionFieldProvider;
import javafx.beans.property.ListProperty;
import javafx.beans.property.MapProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;

import java.lang.reflect.Field;

Expand All @@ -25,23 +29,39 @@
*/
public class ObjectPropertyObserver implements ChangeListener {

private final Object source;

public ObjectPropertyObserver(Object source) {
this.source = source;
for (Field field : new ReflectionFieldProvider().getProperties(source)) {
if (ObservableValue.class.isAssignableFrom(field.getType())) {
try {
field.setAccessible(true);
((ObservableValue) field.get(source)).addListener(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (MapProperty.class.isAssignableFrom(field.getType())) {
((MapProperty) get(field, source)).addListener(new MapChangeListener() {
@Override
public void onChanged(Change change) {
System.out.println(change.toString());
}
});
} else if (ListProperty.class.isAssignableFrom(field.getType())) {
((ListProperty) get(field, source)).addListener(new ListChangeListener() {
@Override
public void onChanged(Change c) {
System.out.println(c.toString());
}
});
} else if (ObservableValue.class.isAssignableFrom(field.getType())) {
((ObservableValue) get(field, source)).addListener(this);
}
}
}

private Object get(Field field, Object source) {
field.setAccessible(true);
try {
return field.get(source);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}

public void changed(ObservableValue observableValue, Object o, Object o1) {
System.out.println(this + ": " + source + ": " + o + "->" + o1);
System.out.println(this + ": " + observableValue + ": " + o + "->" + o1);
}
}
4 changes: 3 additions & 1 deletion demo/src/main/resources/com/dooapp/fxform/Demo.properties
Expand Up @@ -20,4 +20,6 @@ welcome-label=Welcome message
unsubscribe-tooltip=This field is a read-only element with an editable control, so it should be disabled
welcome-tooltip=This field is read-only but its control is not editable, that's why it's not disabled
bigDecimalProperty-tooltip=This field uses a custom adapter provided by the @FormAdapter annotation\nto use a custom formatting with 2 decimals digits
repeatPassword-tooltip=The cross-field validation if performed using a custom class level ConstraintValidator\nand reported at property level. See PasswordMatchValidator
repeatPassword-tooltip=The cross-field validation if performed using a custom class level ConstraintValidator\nand reported at property level. See PasswordMatchValidator
list-tooltip=This field is a ListProperty and can be directly edited within the form
map-tooltip=This field is a MapProperty and can be directly edited within the form

0 comments on commit b8b44ba

Please sign in to comment.