package at.bestsolution.efxclipse.runtime.databinding; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import javafx.beans.InvalidationListener; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.DisposeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.IDisposeListener; import org.eclipse.core.databinding.observable.list.IListChangeListener; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.list.ListChangeEvent; import org.eclipse.core.databinding.observable.list.ListDiffEntry; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; public class AdapterFactory { static class WrappedList<E> implements ObservableList<E> { private List<InvalidationListener> fxInvalidationListeners; private List<ListChangeListener<? super E>> fxChangeListeners; private final IObservableList list; private IChangeListener dbInvalidationListener; private IListChangeListener dbChangeListener; public WrappedList(IObservableList list) { this.list = list; this.list.addDisposeListener(new IDisposeListener() { @Override public void handleDispose(DisposeEvent event) { fxInvalidationListeners.clear(); dbInvalidationListener = null; fxChangeListeners.clear(); dbChangeListener = null; } }); } @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @SuppressWarnings("unchecked") @Override public Iterator<E> iterator() { return list.iterator(); } @Override public Object[] toArray() { return list.toArray(); } @SuppressWarnings("unchecked") @Override public <T> T[] toArray(T[] a) { return (T[]) list.toArray(a); } @Override public boolean add(E e) { return list.add(e); } @Override public boolean remove(Object o) { return list.remove(o); } @Override public boolean containsAll(Collection<?> c) { return list.containsAll(c); } @Override public boolean addAll(Collection<? extends E> c) { return list.addAll(c); } @Override public boolean addAll(int index, Collection<? extends E> c) { return list.addAll(index, c); } @Override public boolean removeAll(Collection<?> c) { return list.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return list.retainAll(c); } @Override public void clear() { list.clear(); } @SuppressWarnings("unchecked") @Override public E get(int index) { return (E) list.get(index); } @SuppressWarnings("unchecked") @Override public E set(int index, E element) { return (E) list.set(index, element); } @SuppressWarnings("unchecked") @Override public void add(int index, E element) { list.add(index, element); } @SuppressWarnings("unchecked") @Override public E remove(int index) { return (E) list.remove(index); } @Override public int indexOf(Object o) { return list.indexOf(o); } @Override public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @SuppressWarnings("unchecked") @Override public ListIterator<E> listIterator() { return list.listIterator(); } @SuppressWarnings("unchecked") @Override public ListIterator<E> listIterator(int index) { return list.listIterator(index); } @SuppressWarnings("unchecked") @Override public List<E> subList(int fromIndex, int toIndex) { return list.subList(fromIndex, toIndex); } @Override public void addListener(InvalidationListener listener) { if( fxInvalidationListeners == null ) { fxInvalidationListeners = new ArrayList<InvalidationListener>(); dbInvalidationListener = new IChangeListener() { @Override public void handleChange(ChangeEvent event) { for( InvalidationListener l : fxInvalidationListeners.toArray(new InvalidationListener[0]) ) { l.invalidated(WrappedList.this); } } }; list.addChangeListener(dbInvalidationListener); } fxInvalidationListeners.add(listener); } @Override public void removeListener(InvalidationListener listener) { if( fxInvalidationListeners != null ) { fxInvalidationListeners.remove(listener); if( fxInvalidationListeners.isEmpty() ) { list.removeChangeListener(dbInvalidationListener); dbInvalidationListener = null; fxInvalidationListeners = null; } } } @Override public boolean addAll(E... elements) { return list.addAll(Arrays.asList(elements)); } @Override public void addListener(ListChangeListener<? super E> listener) { if( fxChangeListeners == null ) { fxChangeListeners = new ArrayList<ListChangeListener<? super E>>(); dbChangeListener = new IListChangeListener() { @SuppressWarnings("unchecked") @Override public void handleListChange(ListChangeEvent event) { final ListDiffEntry[] differences = event.diff.getDifferences(); if( differences.length == 0 ) { return; } //TODO We need to make this perform a lot better by calculating range changes for( ListChangeListener<? super E> l : fxChangeListeners.toArray(new ListChangeListener[0]) ) { Change<E> change = new Change<E>(WrappedList.this) { private int index = -1; private ListDiffEntry current; @Override public int getFrom() { return current.getPosition(); } @Override protected int[] getPermutation() { return new int[0]; } @Override public List<E> getRemoved() { if( ! current.isAddition() ) { return Collections.singletonList((E)current.getElement()); } return Collections.emptyList(); } @Override public int getTo() { if( current.isAddition() ) { return current.getPosition() + 1; } else { return current.getPosition(); } } public boolean wasAdded() { return current.isAddition(); } public boolean wasRemoved() { return ! current.isAddition(); } public boolean wasPermutated() { return false; } public boolean wasReplaced() { return false; } @Override public boolean next() { index++; if( index < differences.length ) { current = differences[index]; return true; } return false; } @Override public void reset() { index = 0; } }; l.onChanged(change); } } }; list.addListChangeListener(dbChangeListener); } fxChangeListeners.add(listener); } @Override public void removeListener(ListChangeListener<? super E> listener) { if( fxChangeListeners != null ) { fxChangeListeners.remove(listener); if( fxChangeListeners.isEmpty() ) { list.removeListChangeListener(dbChangeListener); dbChangeListener = null; fxChangeListeners = null; } } } @Override public void remove(int from, int to) { //TODO Improve performance?? for( int idx = to; idx >= from; idx-- ) { list.remove(idx); } } @Override public boolean removeAll(E... elements) { return list.removeAll(Arrays.asList(elements)); } @Override public boolean retainAll(E... elements) { return list.retainAll(Arrays.asList(elements)); } @Override public boolean setAll(E... elements) { //TODO Improve performance list.clear(); return list.addAll(Arrays.asList(elements)); } @Override public boolean setAll(Collection<? extends E> col) { //TODO Improve performance list.clear(); return list.addAll(col); } } static class WrappedValue<E> implements ObservableValue<E> { private List<InvalidationListener> fxInvalidationListeners; private List<ChangeListener<? super E>> fxChangeListeners; private final IObservableValue value; private IChangeListener dbInvalidationListener; private IValueChangeListener dbChangeListener; public WrappedValue(IObservableValue value) { this.value = value; this.value.addDisposeListener(new IDisposeListener() { @Override public void handleDispose(DisposeEvent event) { fxInvalidationListeners.clear(); dbInvalidationListener = null; fxChangeListeners.clear(); dbChangeListener = null; } }); } @Override public void addListener(InvalidationListener listener) { if( fxInvalidationListeners == null ) { fxInvalidationListeners = new ArrayList<InvalidationListener>(); dbInvalidationListener = new IChangeListener() { @Override public void handleChange(ChangeEvent event) { for( InvalidationListener l : fxInvalidationListeners.toArray(new InvalidationListener[0]) ) { l.invalidated(WrappedValue.this); } } }; value.addChangeListener(dbInvalidationListener); } fxInvalidationListeners.add(listener); } @Override public void removeListener(InvalidationListener listener) { if( fxInvalidationListeners != null ) { fxInvalidationListeners.remove(listener); if( fxInvalidationListeners.isEmpty() ) { value.removeChangeListener(dbInvalidationListener); dbInvalidationListener = null; fxInvalidationListeners = null; } } } @Override public void addListener(ChangeListener<? super E> listener) { if( fxChangeListeners == null ) { fxChangeListeners = new ArrayList<ChangeListener<? super E>>(); dbChangeListener = new IValueChangeListener() { @SuppressWarnings("unchecked") @Override public void handleValueChange(ValueChangeEvent event) { for( ChangeListener<? super E> l : fxChangeListeners.toArray(new ChangeListener[0]) ) { l.changed(WrappedValue.this, (E)event.diff.getOldValue(), (E)event.diff.getNewValue()); } } }; value.addValueChangeListener(dbChangeListener); } fxChangeListeners.add(listener); } @Override public void removeListener(ChangeListener<? super E> listener) { if( fxChangeListeners != null ) { fxChangeListeners.remove(listener); if( fxChangeListeners.isEmpty() ) { value.removeValueChangeListener(dbChangeListener); dbChangeListener = null; fxChangeListeners = null; } } } @SuppressWarnings("unchecked") @Override public E getValue() { return (E) value.getValue(); } } public static <E> ObservableValue<E> adapt(IObservableValue value) { return new WrappedValue<E>(value); } public static <E> ObservableList<E> adapt(final IObservableList list) { return new WrappedList<E>(list); } }