Created
March 3, 2016 14:43
-
-
Save bugabinga/908ca858ffc21337856a to your computer and use it in GitHub Desktop.
Example of an ObservableListWrapper for JavaFx that maps values from a source list to a mapped list according to a user defined transformation. It is the analog of Stream.map for ObservableList.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.bugabinga.javafx.util; | |
import static java.util.stream.Collectors.toList; | |
import java.util.List; | |
import java.util.function.Function; | |
import javafx.collections.ListChangeListener.Change; | |
import javafx.collections.ObservableList; | |
import javafx.collections.ObservableListBase; | |
/** | |
* @author Oliver Jan Krylow <[email protected]> | |
* @date 02.03.2016 | |
*/ | |
public class MappingList<V, E> extends ObservableListBase<E> | |
{ | |
private final List<E> mapped; | |
private final Function<V, E> mapper; | |
public MappingList( final ObservableList<V> source, final Function<V, E> mapper ) | |
{ | |
this.mapper = mapper; | |
mapped = applyMapper( source ); | |
source.addListener( this::sourceChanged ); | |
} | |
private void sourceChanged( final Change<? extends V> c ) | |
{ | |
beginChange(); | |
while ( c.next() ) | |
{ | |
if ( c.wasPermutated() ) | |
{ | |
/* | |
* Order of the list was changed | |
*/ | |
final int[] permutatedElements = new int[c.getTo() - c.getFrom()]; | |
for ( int i = c.getFrom(); i < c.getTo(); ++i ) | |
{ | |
permutatedElements[ i ] = c.getPermutation( i ); | |
mapped.set( i, mapper.apply( c.getList().get( i ) ) ); | |
} | |
nextPermutation( c.getFrom(), c.getTo(), permutatedElements ); | |
} | |
else if ( c.wasUpdated() ) | |
{ | |
/* | |
* Some lists get notified when their elements mutate | |
*/ | |
for ( int i = c.getFrom(); i < c.getTo(); ++i ) | |
{ | |
mapped.set( i, mapper.apply( c.getList().get( i ) ) ); | |
nextUpdate( i ); | |
} | |
} | |
else | |
{ | |
/* | |
* Handles replace,remove and add | |
*/ | |
final List<E> removedElements = applyMapper( c.getRemoved() ); | |
mapped.removeAll( removedElements ); | |
nextRemove( c.getFrom(), removedElements ); | |
final List<E> addedElements = applyMapper( c.getAddedSubList() ); | |
mapped.addAll( c.getFrom(), addedElements ); | |
nextAdd( c.getFrom(), c.getTo() ); | |
} | |
} | |
endChange(); | |
} | |
private List<E> applyMapper( final List<? extends V> list ) | |
{ | |
return list.stream().map( mapper )/*.distinct()*/.collect( toList() ); | |
} | |
@Override | |
public E get( final int index ) | |
{ | |
return mapped.get( index ); | |
} | |
@Override | |
public int size() | |
{ | |
return mapped.size(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.bugabinga.javafx.util; | |
import static org.hamcrest.CoreMatchers.is; | |
import static org.junit.Assert.assertThat; | |
import static org.junit.Assert.assertTrue; | |
import org.junit.Test; | |
import javafx.beans.Observable; | |
import javafx.beans.property.DoubleProperty; | |
import javafx.beans.property.SimpleDoubleProperty; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ObservableList; | |
/** | |
* @author Oliver Jan Krylow <[email protected]> | |
* @date 03.03.2016 | |
* | |
*/ | |
public class MappingListTest | |
{ | |
/** | |
* Test method for | |
* {@link com.isp.canvas.MappingList#MappingList(javafx.collections.ObservableList, java.util.function.Function)} | |
* . | |
*/ | |
@Test | |
public void testMappingList() | |
{ | |
final Double v0 = 0.0; | |
final Double v1 = 1.0; | |
final Double v2 = 1.4; | |
final Double v3 = 2341.5; | |
final Double v4 = 165.99; | |
final ObservableList<Double> valueList = FXCollections.observableArrayList(); | |
final MappingList<Double, String> moneyList = new MappingList<>( valueList, v -> v + " €" ); | |
assertTrue( "Mapped list should be empty.", moneyList.isEmpty() ); | |
// Adding values | |
valueList.add( v0 ); | |
assertThat( moneyList.size(), is( 1 ) ); | |
assertThat( moneyList.get( 0 ), is( "0.0 €" ) ); | |
valueList.add( v1 ); | |
assertThat( moneyList.size(), is( 2 ) ); | |
assertThat( moneyList.get( 1 ), is( "1.0 €" ) ); | |
valueList.add( v2 ); | |
assertThat( moneyList.size(), is( 3 ) ); | |
assertThat( moneyList.get( 2 ), is( "1.4 €" ) ); | |
valueList.add( v3 ); | |
assertThat( moneyList.size(), is( 4 ) ); | |
assertThat( moneyList.get( 3 ), is( "2341.5 €" ) ); | |
valueList.add( v4 ); | |
assertThat( moneyList.size(), is( 5 ) ); | |
assertThat( moneyList.get( 4 ), is( "165.99 €" ) ); | |
//Removing values | |
valueList.remove( 2 ); | |
assertThat( moneyList.size(), is( 4 ) ); | |
assertThat( moneyList.get( 0 ), is( "0.0 €" ) ); | |
assertThat( moneyList.get( 1 ), is( "1.0 €" ) ); | |
assertThat( moneyList.get( 2 ), is( "2341.5 €" ) ); | |
assertThat( moneyList.get( 3 ), is( "165.99 €" ) ); | |
valueList.remove( 0 ); | |
assertThat( moneyList.size(), is( 3 ) ); | |
assertThat( moneyList.get( 0 ), is( "1.0 €" ) ); | |
assertThat( moneyList.get( 1 ), is( "2341.5 €" ) ); | |
assertThat( moneyList.get( 2 ), is( "165.99 €" ) ); | |
valueList.remove( 2 ); | |
assertThat( moneyList.size(), is( 2 ) ); | |
assertThat( moneyList.get( 0 ), is( "1.0 €" ) ); | |
assertThat( moneyList.get( 1 ), is( "2341.5 €" ) ); | |
//Adding values after removing | |
valueList.add( 5.5 ); | |
assertThat( moneyList.size(), is( 3 ) ); | |
assertThat( moneyList.get( 2 ), is( "5.5 €" ) ); | |
valueList.add( 10000.44 ); | |
assertThat( moneyList.size(), is( 4 ) ); | |
assertThat( moneyList.get( 3 ), is( "10000.44 €" ) ); | |
//Changing values | |
valueList.set( 0, 33.33 ); | |
assertThat( moneyList.size(), is( 4 ) ); | |
assertThat( moneyList.get( 0 ), is( "33.33 €" ) ); | |
valueList.set( 3, 1567.111 ); | |
assertThat( moneyList.size(), is( 4 ) ); | |
assertThat( moneyList.get( 3 ), is( "1567.111 €" ) ); | |
//Updating values | |
valueList.replaceAll( v -> v * 2 ); | |
assertThat( moneyList.get( 0 ), is( "66.66 €" ) ); | |
assertThat( moneyList.get( 1 ), is( "4683.0 €" ) ); | |
assertThat( moneyList.get( 2 ), is( "11.0 €" ) ); | |
assertThat( moneyList.get( 3 ), is( "3134.222 €" ) ); | |
// Adding multiple values | |
valueList.addAll( 1.0, 2.0, 3.0, 4.0 ); | |
assertThat( moneyList.get( 4 ), is( "1.0 €" ) ); | |
assertThat( moneyList.get( 5 ), is( "2.0 €" ) ); | |
assertThat( moneyList.get( 6 ), is( "3.0 €" ) ); | |
assertThat( moneyList.get( 7 ), is( "4.0 €" ) ); | |
//Clear the list | |
valueList.clear(); | |
assertThat( moneyList.size(), is( 0 ) ); | |
//Permute the list | |
valueList.addAll( 3.0, 1.0, 2.0 ); | |
valueList.sort( Double::compare ); | |
assertThat( moneyList.get( 0 ), is( "1.0 €" ) ); | |
assertThat( moneyList.get( 1 ), is( "2.0 €" ) ); | |
assertThat( moneyList.get( 2 ), is( "3.0 €" ) ); | |
//Update list elements (element must be Observable) (list needs an extractor, otherwise elements will not be observed for changes) | |
final ObservableList<DoubleProperty> observablePropertyList = FXCollections.observableArrayList( p -> new Observable[]{ p } ); | |
final DoubleProperty d1 = new SimpleDoubleProperty( 0.0 ); | |
final DoubleProperty d2 = new SimpleDoubleProperty( 1.1 ); | |
final DoubleProperty d3 = new SimpleDoubleProperty( 2.2 ); | |
observablePropertyList.addAll( d1, d2, d3 ); | |
final MappingList<DoubleProperty, String> mappedPropertyList = | |
new MappingList<>( observablePropertyList, doubleProperty -> doubleProperty.get() + " $" ); | |
assertThat( mappedPropertyList.get( 0 ), is( "0.0 $" ) ); | |
assertThat( mappedPropertyList.get( 1 ), is( "1.1 $" ) ); | |
assertThat( mappedPropertyList.get( 2 ), is( "2.2 $" ) ); | |
d1.set( 10.1 ); | |
d2.set( 11.11 ); | |
d3.set( 12.12 ); | |
assertThat( mappedPropertyList.get( 0 ), is( "10.1 $" ) ); | |
assertThat( mappedPropertyList.get( 1 ), is( "11.11 $" ) ); | |
assertThat( mappedPropertyList.get( 2 ), is( "12.12 $" ) ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment