Bug 571029: [R-DataEditor] Fix application of filter after refresh

  - Fix invalid bounds in IntervalFilter after refresh

Follow-up-to: 86bfd84b5a74d1968cd4b945935e14d1f4dd0e6b
Change-Id: Ice175f290ef2054aefc3e2a20141101504b2cfbc
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/FilterSet.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/FilterSet.java
index 61b9ef9..7ab8aa6 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/FilterSet.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/FilterSet.java
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 import org.eclipse.core.databinding.observable.Realm;
 import org.eclipse.core.runtime.IStatus;
@@ -55,14 +56,15 @@
 	private final static int STD_DELAY= 1;
 	private final static int NO_DELAY= 2;
 	
+	private final Object updateLock= new Object();
+	
 	private @Nullable RDataTableContentDescription input;
 	
 	private int inFilterUpdate;
 	private boolean isInputFilterUpdate;
 	
-	private final Object updateLock= new Object();
-	private boolean updateScheduled;
-	private final ToolRunnable updateRunnable= new SystemRunnable() {
+	private boolean updateDataScheduled;
+	private final ToolRunnable updateDataRunnable= new SystemRunnable() {
 		
 		@Override
 		public String getTypeId() {
@@ -88,7 +90,7 @@
 			case REMOVING_FROM:
 			case BEING_ABANDONED:
 				synchronized (FilterSet.this.updateLock) {
-					FilterSet.this.updateScheduled= false;
+					FilterSet.this.updateDataScheduled= false;
 					FilterSet.this.updateLock.notifyAll();
 				}
 				break;
@@ -100,13 +102,14 @@
 		
 		@Override
 		public void run(final ToolService service, final ProgressMonitor m) throws StatusException {
-			runUpdate((RToolService)service, m);
+			runUpdateData((RToolService)service, m);
 		}
 		
 	};
 	private boolean updateAll;
+	private boolean inDataUpdate;
 	
-	private ImList<VariableFilter> filters= ImCollections.emptyList();
+	private volatile ImList<VariableFilter> filters= ImCollections.emptyList();
 	private ImList<String> filterNames= ImCollections.emptyList();
 	
 	private final CopyOnWriteIdentityListSet<FilterListener> listeners= new CopyOnWriteIdentityListSet<>();
@@ -115,12 +118,14 @@
 	private long listenerEventStamp;
 	private final Runnable listenerRunnable= this::updateWithinRealm;
 	private final ScheduledRealmRunnable postListenerRunnable;
+	private volatile @Nullable String postListenerEffectiveFilter;
 	
 	private final Realm realm;
 	
 	private boolean enabled;
 	
-	private @Nullable String filterRExpression;
+	private final Object effectiveFilterLock= new Object();
+	private volatile @Nullable String effectiveFilter;
 	
 	
 	public FilterSet(final Realm realm) {
@@ -334,13 +339,13 @@
 			if (all) {
 				this.updateAll= true;
 			}
-			if (this.updateScheduled || this.inFilterUpdate > 0) {
+			if (this.updateDataScheduled || this.inFilterUpdate > 0) {
 				return;
 			}
 			input= this.input;
 			if (input != null) {
-				input.getRHandle().getQueue().add(this.updateRunnable);
-				this.updateScheduled= true;
+				input.getRHandle().getQueue().add(this.updateDataRunnable);
+				this.updateDataScheduled= true;
 			}
 		}
 	}
@@ -350,42 +355,40 @@
 		return (input != null) ? input.getRHandle() : null;
 	}
 	
-	private void runUpdate(final RToolService r, final ProgressMonitor m) {
+	private void runUpdateData(final RToolService r, final ProgressMonitor m) {
 		final RDataTableContentDescription input;
 		final boolean all;
+		final ImList<VariableFilter> filters;
 		synchronized (this.updateLock) {
-			this.updateScheduled= false;
+			this.updateDataScheduled= false;
 			input= this.input;
 			if (this.inFilterUpdate > 0 || input == null || input.getRHandle() != r.getTool()) {
 				return;
 			}
+			filters= this.filters;
 			all= this.updateAll;
 			this.updateAll= false;
+			this.inDataUpdate= true;
 		}
-		final ImList<VariableFilter> filters;
-		synchronized (this) {
-			filters= this.filters;
-		}
-		for (final var filter : filters) {
-			if (all || filter.updateScheduled) {
-				filter.updateScheduled= false;
-				Exception error= null;
-				try {
-					filter.update(r, m);
-				}
-				catch (final StatusException e) {
-					error= e;
-				}
-				catch (final UnexpectedRDataException e) {
-					error= e;
-				}
-				if (error != null) {
-					error.printStackTrace();
-					filter.setStatus(new StatusInfo(IStatus.ERROR, error.getMessage()));
+		try {
+			for (final var filter : filters) {
+				if (all || filter.updateScheduled) {
+					filter.updateScheduled= false;
+					try {
+						filter.updateData(r, m);
+					}
+					catch (final StatusException | UnexpectedRDataException e) {
+						filter.setStatus(new StatusInfo(IStatus.ERROR, e.getMessage()));
+					}
 				}
 			}
 		}
-		updateFilter(true);
+		finally {
+			synchronized (this.updateLock) {
+				this.inDataUpdate= false;
+			}
+		}
+		updateEffectiveFilter(false);
 	}
 	
 	
@@ -412,6 +415,8 @@
 	}
 	
 	private void notifyPostListeners() {
+		this.postListenerEffectiveFilter= this.effectiveFilter;
+		
 		for (final FilterListener listener : this.postListeners) {
 			listener.filterChanged();
 		}
@@ -441,7 +446,7 @@
 	
 	
 	public @Nullable String getFilterRExpression() {
-		return this.filterRExpression;
+		return this.effectiveFilter;
 	}
 	
 	public @Nullable String getFilterRExpression(@Nullable String varExpression, final int nameFlags) {
@@ -471,10 +476,12 @@
 	}
 	
 	public void setEnabled(final boolean enabled) {
-		if (this.enabled == enabled) {
-			return;
+		synchronized (this.effectiveFilterLock) {
+			if (this.enabled == enabled) {
+				return;
+			}
+			this.enabled= enabled;
 		}
-		this.enabled= enabled;
 		notifyListeners(NO_DELAY);
 	}
 	
@@ -482,12 +489,12 @@
 		return this.enabled;
 	}
 	
-	void updateFilter(final boolean delay) {
+	void updateEffectiveFilter(final boolean delay) {
 		final RDataTableContentDescription input;
 		final ImList<VariableFilter> filters;
-		synchronized (this) {
+		synchronized (this.updateLock) {
 			input= this.input;
-			if (this.inFilterUpdate > 0 || input == null ) {
+			if (this.inFilterUpdate > 0 || input == null || this.inDataUpdate) {
 				return;
 			}
 			filters= this.filters;
@@ -502,20 +509,14 @@
 				sb.append(rExpression);
 			}
 		}
-		String filterRExpression;
-		if (sb.length() == 0) {
-			filterRExpression= null; 
-			if (this.filterRExpression == null) {
+		final var filterRExpression= (sb.length() > 0) ? sb.toString() : null;
+		synchronized (this.effectiveFilterLock) {
+			if (Objects.equals(this.effectiveFilter, filterRExpression)
+					&& (delay || this.postListenerEffectiveFilter == this.effectiveFilter) ) {
 				return;
 			}
+			this.effectiveFilter= filterRExpression;
 		}
-		else {
-			filterRExpression= sb.toString();
-			if (filterRExpression.equals(this.filterRExpression)) {
-				return;
-			}
-		}
-		this.filterRExpression= filterRExpression;
 		notifyListeners((delay) ? STD_DELAY : NO_DELAY);
 	}
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/IntervalVariableFilter.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/IntervalVariableFilter.java
index 8670f10..da85832 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/IntervalVariableFilter.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/IntervalVariableFilter.java
@@ -36,12 +36,156 @@
 public class IntervalVariableFilter extends VariableFilter {
 	
 	
-	public static final int MIN_IDX= 0;
-	public static final int MAX_IDX= 1;
-	public static final int NA_IDX= 2;
+	public static abstract class Data {
+		
+		
+		private final boolean hasNA;
+		private final boolean hasMinMax;
+		
+		
+		protected Data(final boolean hasMinMax, final boolean hasNA) {
+			this.hasMinMax= hasMinMax;
+			this.hasNA= hasNA;
+		}
+		
+		
+		public boolean hasMinMax() {
+			return this.hasMinMax;
+		}
+		
+		public abstract Number getMin();
+		public abstract Number getMax();
+		
+		public abstract boolean isValidValue(Number value);
+		public abstract boolean isGreaterThanMin(Number value);
+		public abstract boolean isSmallerThanMax(Number value);
+		
+		public boolean hasNA() {
+			return this.hasNA;
+		}
+		
+	}
+	
+	public static class IntData extends Data {
+		
+		private final int min;
+		private final int max;
+		
+		private IntData(final int min, final int max,
+				final boolean hasNA) {
+			super(true, hasNA);
+			this.min= min;
+			this.max= max;
+		}
+		
+		private IntData(final boolean hasNA) {
+			super(false, hasNA);
+			this.min= 0;
+			this.max= 0;
+		}
+		
+		public int getMinInt() {
+			return this.min;
+		}
+		
+		@Override
+		public Integer getMin() {
+			return this.min;
+		}
+		
+		public int getMaxInt() {
+			return this.max;
+		}
+		
+		@Override
+		public Integer getMax() {
+			return this.max;
+		}
+		
+		@Override
+		public boolean isValidValue(final Number value) {
+			if (value instanceof Integer) {
+				final int v= value.intValue();
+				return (this.min <= v && v <= this.max);
+			}
+			return false;
+		}
+		
+		@Override
+		public boolean isGreaterThanMin(final Number value) {
+			return (value != null && this.min < value.intValue());
+		}
+		
+		@Override
+		public boolean isSmallerThanMax(final Number value) {
+			return (value != null && value.intValue() < this.max);
+		}
+		
+	}
+	
+	public static class NumData extends Data {
+		
+		private final double min;
+		private final double max;
+		
+		private NumData(final double min, final double max,
+				final boolean hasNA) {
+			super(true, hasNA);
+			this.min= min;
+			this.max= max;
+		}
+		
+		private NumData(final boolean hasNA) {
+			super(false, hasNA);
+			this.min= 0;
+			this.max= 0;
+		}
+		
+		public double getMinDouble() {
+			return this.min;
+		}
+		
+		@Override
+		public Double getMin() {
+			return this.min;
+		}
+		
+		public double getMaxDouble() {
+			return this.max;
+		}
+		
+		@Override
+		public Double getMax() {
+			return this.max;
+		}
+		
+		@Override
+		public boolean isValidValue(final Number value) {
+			if (value instanceof Double) {
+				final double v= value.doubleValue();
+				return (this.min <= v && v <= this.max);
+			}
+			return false;
+		}
+		
+		@Override
+		public boolean isGreaterThanMin(final Number value) {
+			return (value != null && this.min < value.doubleValue());
+		}
+		
+		@Override
+		public boolean isSmallerThanMax(final Number value) {
+			return (value != null && value.doubleValue() < this.max);
+		}
+		
+	}
+	
+	private static final int MIN_IDX= 0;
+	private static final int MAX_IDX= 1;
+	private static final int NA_IDX= 2;
 	
 	
-	private @Nullable RStore<? extends Number> minMaxData;
+	private @Nullable Data data;
 	
 	private final IObservableValue<Number> selectedLowerValue;
 	private final IObservableValue<Number> selectedUpperValue;
@@ -71,11 +215,11 @@
 				&& filter.getColumn().getDataStore().getStoreType() == getColumn().getDataStore().getStoreType() ) {
 			final IntervalVariableFilter intervalFilter= (IntervalVariableFilter)filter;
 			runInRealm(() -> {
-				var minMaxData= this.minMaxData;
-				if (minMaxData != null || (minMaxData= intervalFilter.minMaxData) == null) {
+				var minMaxData= this.data;
+				if (minMaxData != null || (minMaxData= intervalFilter.data) == null) {
 					return;
 				}
-				this.minMaxData= minMaxData;
+				this.data= minMaxData;
 				this.selectedLowerValue.setValue(intervalFilter.getSelectedLowerValue().getValue());
 				this.selectedUpperValue.setValue(intervalFilter.getSelectedUpperValue().getValue());
 				this.selectedNA.setValue(intervalFilter.getSelectedNA().getValue());
@@ -86,80 +230,80 @@
 	@Override
 	public void reset() {
 		runInRealm(() -> {
-			final var minMaxData= this.minMaxData;
-			if (minMaxData == null) {
+			final var data= this.data;
+			if (data == null) {
 				return;
 			}
-			this.selectedLowerValue.setValue(minMaxData.get(MIN_IDX));
-			this.selectedUpperValue.setValue(minMaxData.get(MAX_IDX));
+			this.selectedLowerValue.setValue(data.getMin());
+			this.selectedUpperValue.setValue(data.getMax());
 			this.selectedNA.setValue(Boolean.TRUE);
 		});
 	}
 	
 	@Override
-	protected void update(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
+	protected void updateData(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
 		final RDataTableColumn column= getColumn();
 		{	final FunctionCall fcall= r.createFunctionCall("rj:::.getDataIntervalValues"); //$NON-NLS-1$
 			fcall.add(column.getRExpression());
 			
-			final RObject data= fcall.evalData(m);
-			RDataUtils.checkRVector(data);
-			setFilterData((RStore<Number>)RDataUtils.checkData(data.getData(), column.getDataStore().getStoreType()));
-			return;
+			final RObject rData= fcall.evalData(m);
+			RDataUtils.checkRVector(rData);
+			final RStore<Number> rStore= (RStore<Number>)RDataUtils.checkData(
+					rData.getData(), column.getDataStore().getStoreType() );
+			RDataUtils.checkLengthGreaterOrEqual(rStore, 3);
+			final boolean hasMinMax= !rStore.isMissing(MIN_IDX) && !rStore.isMissing(MAX_IDX);
+			final boolean hasNA= rStore.getLogi(NA_IDX);
+			if (rStore.getStoreType() == RStore.NUMERIC) {
+				setFilterData((hasMinMax) ?
+						new NumData(rStore.getNum(MIN_IDX), rStore.getNum(MAX_IDX), hasNA) :
+						new NumData(hasNA) );
+			}
+			else {
+				setFilterData((hasMinMax) ?
+						new IntData(rStore.getInt(MIN_IDX), rStore.getInt(MAX_IDX), hasNA) :
+						new IntData(hasNA) );
+			}
 		}
 	}
 	
 	@Override
 	protected void setStatus(final IStatus status) {
 		runInRealm(() -> {
-			this.minMaxData= null;
+			this.data= null;
 			doSetStatus(status);
+			updateFilter(SCHEDULE);
 			notifyListeners();
 		});
 	}
 	
-	protected void setFilterData(final RStore<? extends Number> minMaxData) {
+	protected void setFilterData(final Data data) {
 		runInRealm(() -> {
-			final boolean wasMin;
-			{	final Object value= this.selectedLowerValue.getValue();
-				wasMin= (value == null || value.equals(minMaxData.get(MIN_IDX)));
+			final var prevData= this.data;
+			this.data= data;
+			{	final Number value= this.selectedLowerValue.getValue();
+				if (!data.isValidValue(value)
+						|| prevData == null || value.equals(prevData.getMin()) ) {
+					this.selectedLowerValue.setValue(data.getMin());
+				}
 			}
-			final boolean wasMax;
-			{	final Object value= this.selectedLowerValue.getValue();
-				wasMax= (value == null || value.equals(minMaxData.get(MAX_IDX)));
-			}
-			this.minMaxData= minMaxData;
-			if (wasMin) {
-				this.selectedLowerValue.setValue(minMaxData.get(MIN_IDX));
-			}
-			if (wasMax) {
-				this.selectedUpperValue.setValue(minMaxData.get(MAX_IDX));
+			{	final Number value= this.selectedLowerValue.getValue();
+				if (!data.isValidValue(value)
+						|| prevData == null || value.equals(prevData.getMax()) ) {
+					this.selectedUpperValue.setValue(data.getMax());
+				}
 			}
 			clearStatus();
+			updateFilter(SCHEDULE);
 			notifyListeners();
 		});
 	}
 	
-	private static boolean isSmaller(final Number e1, final Number e2) {
-		if (e1 instanceof Integer) {
-			return (((Integer)e1).intValue() < ((Integer)e2).intValue());
-		}
-		return (e1.doubleValue() < e2.doubleValue());
-	}
-	
-	private static boolean isGreater(final Object e1, final Object e2) {
-		if (e1 instanceof Integer) {
-			return (((Integer)e1).intValue() > ((Integer)e2).intValue());
-		}
-		return (((Number)e1).doubleValue() > ((Number)e2).doubleValue());
-	}
-	
 	@Override
 	protected @Nullable String createFilter(final String varExpression) {
-		final var minMaxData= this.minMaxData;
+		final var data= this.data;
 		final Number lower;
 		final Number upper;
-		if (minMaxData == null
+		if (data == null
 				|| (lower= this.selectedLowerValue.getValue()) == null
 				|| (upper= this.selectedUpperValue.getValue()) == null) {
 			return null;
@@ -170,22 +314,24 @@
 		int num= 0;
 		int na= 0;
 		
-		if (isGreater(lower, minMaxData.get(MIN_IDX))) {
-			sb.append(varExpression);
-			sb.append(" >= "); //$NON-NLS-1$
-			sb.append(lower);
-			num++;
-		}
-		if (isSmaller(upper, minMaxData.get(MAX_IDX))) {
-			if (sb.length() > 1) {
-				sb.append(" & "); //$NON-NLS-1$
+		if (data.hasMinMax()) {
+			if (data.isGreaterThanMin(lower)) {
+				sb.append(varExpression);
+				sb.append(" >= "); //$NON-NLS-1$
+				sb.append(lower);
+				num++;
 			}
-			sb.append(varExpression);
-			sb.append(" <= "); //$NON-NLS-1$
-			sb.append(upper);
-			num++;
+			if (data.isSmallerThanMax(upper)) {
+				if (sb.length() > 1) {
+					sb.append(" & "); //$NON-NLS-1$
+				}
+				sb.append(varExpression);
+				sb.append(" <= "); //$NON-NLS-1$
+				sb.append(upper);
+				num++;
+			}
 		}
-		if (minMaxData.getLogi(NA_IDX)) {
+		if (data.hasNA()) {
 			na= (this.selectedNA.getValue()) ? 1 : -1;
 		}
 		if (num > 0 || na < 0) {
@@ -211,8 +357,8 @@
 	}
 	
 	
-	public @Nullable RStore<? extends Number> getMinMaxData() {
-		return this.minMaxData;
+	public @Nullable Data getData() {
+		return this.data;
 	}
 	
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/LevelVariableFilter.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/LevelVariableFilter.java
index e83ed7f..81d3597 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/LevelVariableFilter.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/LevelVariableFilter.java
@@ -87,7 +87,7 @@
 	}
 	
 	@Override
-	protected void update(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
+	protected void updateData(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
 		final RDataTableColumn column= getColumn();
 		{	final FunctionCall fcall= r.createFunctionCall("rj:::.getDataLevelValues"); //$NON-NLS-1$
 			fcall.add(column.getRExpression());
@@ -105,7 +105,6 @@
 			else {
 				setFilterData(RDataUtils.checkData(data.getData(), column.getDataStore().getStoreType()));
 			}
-			return;
 		}
 	}
 	
@@ -114,6 +113,7 @@
 		runInRealm(() -> {
 			this.availableValues= NO_VALUES;
 			doSetStatus(status);
+			updateFilter(SCHEDULE);
 			notifyListeners();
 		});
 	}
@@ -123,6 +123,7 @@
 			if (!values.equals(this.availableValues) || getStatus() != null) {
 				this.availableValues= values;
 				clearStatus();
+				updateFilter(SCHEDULE);
 				notifyListeners();
 			}
 		});
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/TextVariableFilter.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/TextVariableFilter.java
index 17a49e3..7899ee7 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/TextVariableFilter.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/TextVariableFilter.java
@@ -90,7 +90,7 @@
 	}
 	
 	@Override
-	protected void update(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
+	protected void updateData(final RToolService r, final ProgressMonitor m) throws StatusException, UnexpectedRDataException {
 		final RDataTableColumn column= getColumn();
 		TextSearchType searchType;
 		String searchText;
@@ -141,6 +141,7 @@
 	protected void setStatus(final IStatus message) {
 		runInRealm(() -> {
 			doSetStatus(message);
+			updateFilter(SCHEDULE);
 			notifyListeners();
 		});
 	}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/VariableFilter.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/VariableFilter.java
index 91d1dc4..dd28263 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/VariableFilter.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilter/VariableFilter.java
@@ -42,16 +42,19 @@
 	
 	protected final static RCharacter32Store NO_VALUES= new RCharacter32Store();
 	
+	static final int SCHEDULE=             1 << 0;
+	static final int SCHEDULE_WITH_DELAY=  SCHEDULE | 1 << 1;
+	
 	private class BindingListener implements IValueChangeListener<Object>, ISetChangeListener<Object> {
 		
 		@Override
 		public void handleSetChange(final SetChangeEvent<?> event) {
-			updateFilter(1);
+			updateFilter(SCHEDULE_WITH_DELAY);
 		}
 		
 		@Override
 		public void handleValueChange(final ValueChangeEvent<?> event) {
-			updateFilter(1);
+			updateFilter(SCHEDULE_WITH_DELAY);
 		}
 		
 	}
@@ -117,7 +120,10 @@
 			return;
 		}
 		this.filterRExpression= rExpression;
-		this.filterSet.updateFilter((flag & 1) == 1);
+		
+		if ((flag & SCHEDULE) != 0) {
+			this.filterSet.updateEffectiveFilter((flag & SCHEDULE_WITH_DELAY) == SCHEDULE_WITH_DELAY);
+		}
 	}
 	
 	protected abstract @Nullable String createFilter(String varExpression);
@@ -131,7 +137,7 @@
 		this.listener= listener;
 	}
 	
-	protected abstract void update(RToolService r, ProgressMonitor m) throws StatusException, UnexpectedRDataException;
+	protected abstract void updateData(RToolService r, ProgressMonitor m) throws StatusException, UnexpectedRDataException;
 	
 	protected void notifyListeners() {
 		final var listener= this.listener;
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilterview/IntervalFilterClient.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilterview/IntervalFilterClient.java
index a491181..c27d0dd 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilterview/IntervalFilterClient.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/datafilterview/IntervalFilterClient.java
@@ -16,10 +16,6 @@
 
 import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
 
-import static org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter.MAX_IDX;
-import static org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter.MIN_IDX;
-import static org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter.NA_IDX;
-
 import org.eclipse.core.databinding.conversion.IConverter;
 import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
 import org.eclipse.jface.resource.JFaceResources;
@@ -38,6 +34,8 @@
 import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
 
 import org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter;
+import org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter.IntData;
+import org.eclipse.statet.internal.r.ui.datafilter.IntervalVariableFilter.NumData;
 import org.eclipse.statet.rj.data.RStore;
 
 
@@ -146,25 +144,25 @@
 	
 	@Override
 	protected void updateInput() {
-		final var minMaxData= this.filter.getMinMaxData();
-		if (minMaxData == null) {
+		final var data= this.filter.getData();
+		if (data == null) {
 			setEnabled(false, false);
 			return;
 		}
 		
 		if (this.lowerUpperGroup instanceof IntValue2Double2TextBinding.LowerUpperGroup) {
-			final double min= minMaxData.getNum(MIN_IDX);
-			final double max= minMaxData.getNum(MAX_IDX);
-			setEnabled((min != max), minMaxData.getLogi(NA_IDX));
-			((IntValue2Double2TextBinding.LowerUpperGroup)this.lowerUpperGroup).setMinMax(
-					min, max );
+			final NumData numData= (NumData)data;
+			final double min= numData.getMinDouble();
+			final double max= numData.getMaxDouble();
+			setEnabled(numData.hasMinMax() && min != max, data.hasNA());
+			((IntValue2Double2TextBinding.LowerUpperGroup)this.lowerUpperGroup).setMinMax(min, max);
 		}
 		else {
-			final int min= minMaxData.getInt(MIN_IDX);
-			final int max= minMaxData.getInt(MAX_IDX);
-			setEnabled((min != max), minMaxData.getLogi(NA_IDX));
-			((IntValue2TextBinding.LowerUpperGroup)this.lowerUpperGroup).setMinMax(
-					min, max );
+			final IntData intData= (IntData)data;
+			final int min= intData.getMinInt();
+			final int max= intData.getMaxInt();
+			setEnabled(intData.hasMinMax() && min != max, data.hasNA());
+			((IntValue2TextBinding.LowerUpperGroup)this.lowerUpperGroup).setMinMax(min, max);
 		}
 	}