From cd202c7c9a0b40f1e66b6d0fe8fd93fffbec5435 Mon Sep 17 00:00:00 2001 From: rstein Date: Mon, 8 Mar 2021 12:12:41 +0100 Subject: [PATCH] fixes autoranging for CircularDoubleErrorDataSet (issue #378) + additional unit-tests and follow-up fixes --- .../de/gsi/dataset/spi/AveragingDataSet.java | 65 +++++---- .../spi/CircularDoubleErrorDataSet.java | 90 +++++++++---- .../dataset/spi/FifoDoubleErrorDataSet.java | 69 +++++++--- .../de/gsi/dataset/spi/FragmentedDataSet.java | 20 +-- .../dataset/spi/MultiDimDoubleDataSet.java | 43 +++++- .../de/gsi/dataset/spi/RollingDataSet.java | 43 +----- .../de/gsi/dataset/spi/WrappedDataSet.java | 11 +- .../spi/CircularDoubleErrorDataSetTests.java | 53 ++++++-- .../gsi/dataset/spi/DataSetEqualityTests.java | 34 ++--- .../gsi/dataset/spi/GenericDataSetTests.java | 125 ++++++++++++++++++ .../gsi/dataset/spi/GenericNotifyTests.java | 37 ------ 11 files changed, 385 insertions(+), 205 deletions(-) create mode 100644 chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericDataSetTests.java delete mode 100644 chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericNotifyTests.java diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/AveragingDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/AveragingDataSet.java index 878d43473..7efc1f7fd 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/AveragingDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/AveragingDataSet.java @@ -2,8 +2,10 @@ import java.util.ArrayDeque; +import de.gsi.dataset.AxisDescription; import de.gsi.dataset.DataSet; import de.gsi.dataset.event.AddedDataEvent; +import de.gsi.dataset.event.UpdatedDataEvent; /** * TODO: Change to ErrorDataSet and calculate standard deviation. @@ -13,10 +15,17 @@ public class AveragingDataSet extends AbstractDataSet implements DataSet { private static final long serialVersionUID = 1L; private int averageSize = 1; - private int fuzzyCount = 0; + private int fuzzyCount; private InternalDataSet dataset; private final ArrayDeque deque = new ArrayDeque<>(); + /** + * @param name data set name (assumes 2-dim DataSet + */ + public AveragingDataSet(final String name) { + this(name, 2, 0); + } + /** * @param name data set name * @param nDims number of dimensions @@ -150,12 +159,12 @@ public InternalDataSet(DataSet ds) { ds.getDataCount(), true); } - public boolean isCompatible(DataSet d) { - return Math.abs(super.getDataCount() - d.getDataCount()) <= fuzzyCount; + public boolean isIncompatible(DataSet d) { + return Math.abs(super.getDataCount() - d.getDataCount()) > fuzzyCount; } public void opAdd(DataSet d) { - if (!isCompatible(d)) { + if (isIncompatible(d)) { throw new IllegalArgumentException("Datasets do not match"); } @@ -172,7 +181,7 @@ public void opScale(double f) { } public void opSub(DataSet d) { - if (!isCompatible(d)) { + if (isIncompatible(d)) { throw new IllegalArgumentException("Datasets do not match"); } yValues.size(d.getDataCount()); @@ -189,26 +198,36 @@ public double getValue(int dimIndex, double... x) { @Override public DataSet set(final DataSet other, final boolean copy) { - if (other instanceof AveragingDataSet) { - this.fuzzyCount = ((AveragingDataSet) other).getFuzzyCount(); - this.averageSize = ((AveragingDataSet) other).getAverageSize(); + lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> { + if (other instanceof AveragingDataSet) { + this.fuzzyCount = ((AveragingDataSet) other).getFuzzyCount(); + this.averageSize = ((AveragingDataSet) other).getAverageSize(); + if (copy) { + this.clear(); + ((AveragingDataSet) other).deque.forEach(ds -> this.add(new DefaultDataSet(ds))); + } else { + this.dataset = ((AveragingDataSet) other).dataset; + this.deque.clear(); + this.deque.addAll(((AveragingDataSet) other).deque); + } + getAxisDescriptions().forEach(AxisDescription::clear); + for (int dim = 0; dim < getDimension(); dim++) { + recomputeLimits(dim); + } + return; + } + // non AveragingDataSet: add the other data set as a single data set + this.clear(); if (copy) { - this.clear(); - ((AveragingDataSet) other).deque.forEach(ds -> this.add(new DefaultDataSet(ds))); + this.add(new DefaultDataSet(other)); } else { - this.dataset = ((AveragingDataSet) other).dataset; - this.deque.clear(); - this.deque.addAll(((AveragingDataSet) other).deque); + this.add(other); } - return this; - } - // non AveragingDataSet: add the other data set as a single data set - this.clear(); - if (copy) { - this.add(new DefaultDataSet(other)); - } else { - this.add(other); - } - return this; + getAxisDescriptions().forEach(AxisDescription::clear); + for (int dim = 0; dim < getDimension(); dim++) { + recomputeLimits(dim); + } + })); + return fireInvalidated(new UpdatedDataEvent(this, "set(DataSet, boolean=" + copy + ")")); } } diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/CircularDoubleErrorDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/CircularDoubleErrorDataSet.java index 8d0e9ef6e..4f246b792 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/CircularDoubleErrorDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/CircularDoubleErrorDataSet.java @@ -6,6 +6,7 @@ import de.gsi.dataset.DataSetError; import de.gsi.dataset.event.AddedDataEvent; import de.gsi.dataset.event.RemovedDataEvent; +import de.gsi.dataset.event.UpdatedDataEvent; import de.gsi.dataset.utils.AssertUtils; import de.gsi.dataset.utils.CircularBuffer; import de.gsi.dataset.utils.DoubleCircularBuffer; @@ -26,18 +27,18 @@ public class CircularDoubleErrorDataSet extends AbstractErrorDataSetCircularDoubleErrorDataSet. * * @param name name of this DataSet. - * @param initalSize maximum circular buffer capacity + * @param initialSize maximum circular buffer capacity * @throws IllegalArgumentException if name is null */ - public CircularDoubleErrorDataSet(final String name, final int initalSize) { + public CircularDoubleErrorDataSet(final String name, final int initialSize) { super(name, 2, ErrorType.NO_ERROR, ErrorType.ASYMMETRIC); - AssertUtils.gtEqThanZero("initalSize", initalSize); - xValues = new DoubleCircularBuffer(initalSize); - yValues = new DoubleCircularBuffer(initalSize); - yErrorsPos = new DoubleCircularBuffer(initalSize); - yErrorsNeg = new DoubleCircularBuffer(initalSize); - dataLabels = new CircularBuffer<>(initalSize); - dataStyles = new CircularBuffer<>(initalSize); + AssertUtils.gtEqThanZero("initialSize", initialSize); + xValues = new DoubleCircularBuffer(initialSize); + yValues = new DoubleCircularBuffer(initialSize); + yErrorsPos = new DoubleCircularBuffer(initialSize); + yErrorsNeg = new DoubleCircularBuffer(initialSize); + dataLabels = new CircularBuffer<>(initialSize); + dataStyles = new CircularBuffer<>(initialSize); } /** @@ -87,9 +88,10 @@ public CircularDoubleErrorDataSet add(final double x, final double y, final doub dataLabels.put(label); dataStyles.put(style); + // assumes in X sorted data range getAxisDescription(DIM_X).setMin(xValues.get(0)); getAxisDescription(DIM_X).setMax(xValues.get(xValues.available() - 1)); - getAxisDescription(DIM_Y).add(y); + getAxisDescription(DIM_Y).clear(); }); return fireInvalidated(new AddedDataEvent(this)); @@ -112,24 +114,45 @@ public CircularDoubleErrorDataSet add(final double[] xVals, final double[] yVals AssertUtils.notNull("Y coordinates", yVals); AssertUtils.notNull("Y error neg", yErrNeg); AssertUtils.notNull("Y error pos", yErrPos); - AssertUtils.equalDoubleArrays(xVals, yVals); - AssertUtils.equalDoubleArrays(xVals, yErrNeg); - AssertUtils.equalDoubleArrays(xVals, yErrPos); + final int dataCount = Math.min(Math.min(xVals.length, yVals.length), Math.min(yErrNeg.length, yErrPos.length)); + return add(xVals, yVals, yErrNeg, yErrPos, dataCount); + } - lock().writeLockGuard(() -> { - this.xValues.put(xVals, xVals.length); - this.yValues.put(yVals, yVals.length); - this.yErrorsNeg.put(yErrNeg, yErrNeg.length); - this.yErrorsPos.put(yErrPos, yErrPos.length); - dataLabels.put(new String[yErrPos.length], yErrPos.length); - dataStyles.put(new String[yErrPos.length], yErrPos.length); + /** + *

+ * Initialises the data set with specified data. + *

+ * Note: The method copies values from specified double arrays. + * + * @param xVals the new x coordinates + * @param yVals the new y coordinates + * @param yErrNeg the +dy errors + * @param yErrPos the -dy errors + * @param dataCount maximum number of data points to copy (e.g. in case array store more than needs to be copied) + * @return itself + */ + public CircularDoubleErrorDataSet add(final double[] xVals, final double[] yVals, final double[] yErrNeg, final double[] yErrPos, final int dataCount) { + AssertUtils.notNull("X coordinates", xVals); + AssertUtils.notNull("Y coordinates", yVals); + AssertUtils.notNull("Y error neg", yErrNeg); + AssertUtils.notNull("Y error pos", yErrPos); + AssertUtils.gtOrEqual("X coordinates", dataCount, xVals.length); + AssertUtils.gtOrEqual("Y coordinates", dataCount, yVals.length); + AssertUtils.gtOrEqual("Y error neg", dataCount, yErrNeg.length); + AssertUtils.gtOrEqual("Y error pos", dataCount, yErrPos.length); + lock().writeLockGuard(() -> { + this.xValues.put(xVals, dataCount); + this.yValues.put(yVals, dataCount); + this.yErrorsNeg.put(yErrNeg, dataCount); + this.yErrorsPos.put(yErrPos, dataCount); + dataLabels.put(new String[yVals.length], dataCount); + dataStyles.put(new String[yVals.length], dataCount); + + // assumes in X sorted data range getAxisDescription(DIM_X).setMin(xValues.get(0)); getAxisDescription(DIM_X).setMax(xValues.get(xValues.available() - 1)); - for (int i = 0; i < yVals.length; i++) { - getAxisDescription(DIM_Y).add(yVals[i] + yErrPos[i]); - getAxisDescription(DIM_Y).add(yVals[i] - yErrNeg[i]); - } + getAxisDescription(DIM_Y).clear(); }); return fireInvalidated(new AddedDataEvent(this)); @@ -206,6 +229,23 @@ public CircularDoubleErrorDataSet reset() { @Override public DataSet set(final DataSet other, final boolean copy) { - throw new UnsupportedOperationException("copy setting transposed data set is not implemented"); + lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> { + this.reset(); + if (other.getDataCount() == 0) { + return; + } + // copy data + final int count = other.getDataCount(); + if (other instanceof DataSetError) { + this.add(other.getValues(DIM_X), other.getValues(DIM_Y), ((DataSetError) other).getErrorsNegative(DIM_Y), ((DataSetError) other).getErrorsPositive(DIM_Y), other.getDataCount()); + } else { + this.add(other.getValues(DIM_X), other.getValues(DIM_Y), new double[count], new double[count], other.getDataCount()); + } + + copyMetaData(other); + copyDataLabelsAndStyles(other, copy); + copyAxisDescription(other); + })); + return fireInvalidated(new UpdatedDataEvent(this, "set(DataSet, boolean=" + copy + ")")); } } diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FifoDoubleErrorDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FifoDoubleErrorDataSet.java index ea4352792..4bd7abb59 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FifoDoubleErrorDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FifoDoubleErrorDataSet.java @@ -9,7 +9,9 @@ import de.gsi.dataset.DataSetError; import de.gsi.dataset.event.AddedDataEvent; import de.gsi.dataset.event.RemovedDataEvent; +import de.gsi.dataset.event.UpdatedDataEvent; import de.gsi.dataset.spi.utils.DoublePointError; +import de.gsi.dataset.utils.AssertUtils; import de.gsi.dataset.utils.LimitedQueue; /** @@ -21,37 +23,37 @@ public class FifoDoubleErrorDataSet extends AbstractErrorDataSet data; - protected double maxDistance = Double.MAX_VALUE; + protected double maxDistance; /** * Creates a new instance of FifoDoubleErrorDataSet. * * @param name name of this DataSet. - * @param initalSize maximum circular buffer capacity + * @param initialSize maximum circular buffer capacity * @throws IllegalArgumentException if name is null */ - public FifoDoubleErrorDataSet(final String name, final int initalSize) { - this(name, initalSize, Double.MAX_VALUE); + public FifoDoubleErrorDataSet(final String name, final int initialSize) { + this(name, initialSize, Double.MAX_VALUE); } /** * Creates a new instance of FifoDoubleErrorDataSet. * * @param name name of this DataSet. - * @param initalSize maximum circular buffer capacity + * @param initialSize maximum circular buffer capacity * @param maxDistance maximum range before data points are being dropped * @throws IllegalArgumentException if name is null */ - public FifoDoubleErrorDataSet(final String name, final int initalSize, final double maxDistance) { + public FifoDoubleErrorDataSet(final String name, final int initialSize, final double maxDistance) { super(name, 2, ErrorType.NO_ERROR, ErrorType.SYMMETRIC); - if (initalSize <= 0) { - throw new IllegalArgumentException("negative or zero initalSize = " + initalSize); + if (initialSize <= 0) { + throw new IllegalArgumentException("negative or zero initialSize = " + initialSize); } if (maxDistance <= 0) { throw new IllegalArgumentException("negative or zero maxDistance = " + maxDistance); } this.maxDistance = maxDistance; - data = new LimitedQueue<>(initalSize); + data = new LimitedQueue<>(initialSize); } /** @@ -109,6 +111,27 @@ public FifoDoubleErrorDataSet add(final double x, final double y, final double y return this; } + /** + *

+ * Initialises the data set with specified data. + *

+ * Note: The method copies values from specified double arrays. + * + * @param xVals the new x coordinates + * @param yVals the new y coordinates + * @param yErrNeg the +dy errors + * @param yErrPos the -dy errors + * @return itself + */ + public FifoDoubleErrorDataSet add(final double[] xVals, final double[] yVals, final double[] yErrNeg, final double[] yErrPos) { + AssertUtils.notNull("X coordinates", xVals); + AssertUtils.notNull("Y coordinates", yVals); + AssertUtils.notNull("Y error neg", yErrNeg); + AssertUtils.notNull("Y error pos", yErrPos); + final int dataCount = Math.min(Math.min(xVals.length, yVals.length), Math.min(yErrNeg.length, yErrPos.length)); + return add(xVals, yVals, yErrNeg, yErrPos, dataCount); + } + /** *

* Initialises the data set with specified data. @@ -119,12 +142,12 @@ public FifoDoubleErrorDataSet add(final double x, final double y, final double y * @param yValues the new y coordinates * @param yErrorsNeg the -dy errors * @param yErrorsPos the +dy errors + * @param dataCount maximum number of data points to copy (e.g. in case array store more than needs to be copied) * @return itself */ - public FifoDoubleErrorDataSet add(final double[] xValues, final double[] yValues, final double[] yErrorsNeg, - final double[] yErrorsPos) { + public FifoDoubleErrorDataSet add(final double[] xValues, final double[] yValues, final double[] yErrorsNeg, final double[] yErrorsPos, final int dataCount) { lock().writeLockGuard(() -> { - for (int i = 0; i < xValues.length; i++) { + for (int i = 0; i < dataCount; i++) { this.add(xValues[i], yValues[i], yErrorsNeg[i], yErrorsPos[i]); } }); @@ -226,9 +249,9 @@ protected static class DataBlob extends DoublePointError { protected String style; protected String tag; - protected DataBlob(final double x, final double y, final double errorYNeg, final double errorYPos, final String tag, - final String style) { - super(x, y, errorYNeg, errorYPos); + protected DataBlob(final double x, final double y, final double errorYNeg, final double errorYPos, final String tag, final String style) { + //noinspection SuspiciousNameCombination + super(x, y, errorYNeg, errorYPos); // NOPMD NOSONAR - super's x/y error is reinterpreted as +ey -ey in this class this.tag = tag; this.style = style; } @@ -244,6 +267,20 @@ public String getStyle() { @Override public DataSet set(final DataSet other, final boolean copy) { - throw new UnsupportedOperationException("copy setting transposed data set is not implemented"); + lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> { + this.reset(); + // copy data + final int count = other.getDataCount(); + if (other instanceof DataSetError) { + this.add(other.getValues(DIM_X), other.getValues(DIM_Y), ((DataSetError) other).getErrorsNegative(DIM_Y), ((DataSetError) other).getErrorsPositive(DIM_Y), other.getDataCount()); + } else { + this.add(other.getValues(DIM_X), other.getValues(DIM_Y), new double[count], new double[count], other.getDataCount()); + } + + copyMetaData(other); + copyDataLabelsAndStyles(other, copy); + copyAxisDescription(other); + })); + return fireInvalidated(new UpdatedDataEvent(this, "set(DataSet, boolean=" + copy + ")")); } } diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FragmentedDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FragmentedDataSet.java index 0fea348ef..abb9b332c 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FragmentedDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/FragmentedDataSet.java @@ -1,7 +1,3 @@ -/***************************************************************************** - * * Chart Common - dataset consisting of other dataset fragments * * modified: 2019-01-23 Harald Braeuning * * - ****************************************************************************/ - package de.gsi.dataset.spi; import java.util.ArrayList; @@ -19,10 +15,6 @@ public class FragmentedDataSet extends AbstractDataSet implements DataSet2D { private static final long serialVersionUID = 2540953806461866839L; protected int dataCount; - // protected double xmin; - // protected double xmax; - // protected double ymin; - // protected double ymax; protected final ArrayList list = new ArrayList<>(); /** @@ -59,7 +51,7 @@ public void add(final double[] xValues, final double[] yValues) { // TODO: harald -> please check whether this is supposed to be deep copy // (true) or by reference (false) hand over (assumption here is 'by reference/pointer' -> remove this comment // once checked - final DoubleDataSet set = new DoubleDataSet(String.format("Fragement #%d", list.size() + 1), xValues, yValues, + final DoubleDataSet set = new DoubleDataSet(String.format("Fragment #%d", list.size() + 1), xValues, yValues, xValues.length, false); add(set); } @@ -133,6 +125,14 @@ public double[] getValues(final int dimIndex) { @Override public DataSet set(final DataSet other, final boolean copy) { - throw new UnsupportedOperationException("copy setting transposed data set is not implemented"); + lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> { + clear(); + add(other); + + copyMetaData(other); + copyDataLabelsAndStyles(other, copy); + copyAxisDescription(other); + })); + return fireInvalidated(new UpdatedDataEvent(this, "set(DataSet, boolean=" + copy + ")")); } } diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/MultiDimDoubleDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/MultiDimDoubleDataSet.java index 6c114a07a..b6b289790 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/MultiDimDoubleDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/MultiDimDoubleDataSet.java @@ -336,12 +336,23 @@ public MultiDimDoubleDataSet resize(final int size) { @Override public MultiDimDoubleDataSet set(final DataSet other, final boolean copy) { lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> { + this.resize(0); + // copy data double[][] data = new double[other.getDimension()][]; - for (int i = 0; i < other.getDimension(); i++) { - data[i] = other.getValues(i); + for (int dim = 0; dim < other.getDimension(); dim++) { + data[dim] = other.getValues(dim); + } + if (other.getDimension() == 2) { + this.set(data, other.getDataCount(), copy); + } else { + this.set(data, copy); + } + + for (int dim = 0; dim < other.getDimension(); dim++) { + recomputeLimits(dim); } - this.set(data, copy); + // deep copy data point labels and styles for (int index = 0; index < other.getDataCount(); index++) { final String label = other.getDataLabel(index); @@ -384,10 +395,28 @@ public MultiDimDoubleDataSet set(final double[][] values) { * @return itself */ public MultiDimDoubleDataSet set(final double[][] values, final boolean copy) { - int dataMaxIndex = Integer.MAX_VALUE; + int dataCount = Integer.MAX_VALUE; + for (int i = 0; i < this.values.length; i++) { + AssertUtils.notNull(i + "-th coordinates", values[i]); + dataCount = Math.min(dataCount, values[i].length); + } + return this.set(values, dataCount, copy); + } + + /** + *

+ * Initialises the data set with specified data. + *

+ * Note: The method copies values from specified double arrays. + * + * @param values coordinates + * @param copy true: makes an internal copy, false: use the pointer as is (saves memory allocation + * @param dataCount maximum number of data points to copy (e.g. in case array store more than needs to be copied) + * @return itself + */ + public MultiDimDoubleDataSet set(final double[][] values, final int dataCount, final boolean copy) { for (int i = 0; i < this.values.length; i++) { - AssertUtils.notNull("X coordinates", values[i]); - dataMaxIndex = Math.min(dataMaxIndex, values[i].length); + AssertUtils.notNull(i + "-th coordinates", values[i]); } lock().writeLockGuard(() -> { @@ -396,7 +425,7 @@ public MultiDimDoubleDataSet set(final double[][] values, final boolean copy) { if (copy) { resize(0); for (int i = 0; i < this.values.length; i++) { - this.values[i].addElements(0, values[i]); + this.values[i].addElements(0, values[i], 0, dataCount); } } else { for (int i = 0; i < this.values.length; i++) { diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/RollingDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/RollingDataSet.java index e980f192b..a9a1b66db 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/RollingDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/RollingDataSet.java @@ -1,11 +1,3 @@ -/** - * *************************************************************************** - * * - * BI Common - Rolling data set * - * * - * modified: 2018-08-24 Harald Braeuning * - * * - */ package de.gsi.dataset.spi; import de.gsi.dataset.AxisDescription; @@ -28,42 +20,9 @@ public RollingDataSet(final String name) { super(name); } - // @Override - // public void add(final DataSet set) - // { - // ArrayList tmp = new ArrayList<>(list); - // while (tmp.size() >= depth.get()) - // { - // tmp.remove(0); - // } - // list.clear(); - // dataCount = 0; - // /* - // * Because AbstractDataSet does not allow editing of its data, we have to - // * copy the dataset to shift the y values - // */ - // for (final DataSet ds : tmp) - // { - // double[] x = new double[ds.getDataCount()]; - // double[] y = new double[ds.getDataCount()]; - // for (int i = 0; i < ds.getDataCount(); i++) - // { - // x[i] = ds.getX(i) - lastLength; - // y[i] = ds.getY(i); - // } - // list.add(new DoubleDataSet(ds.getName(), x, y, false)); - // dataCount += ds.getDataCount(); - // } - // list.add(set); - // dataCount += set.getDataCount(); - // lastLength = set.getXMax(); - // computeLimits(); - // fireInvalidated(); - // } - @Override public void add(final DataSet set) { - while (list.size() >= depth) { + while (!list.isEmpty() && list.size() >= depth) { final DataSet ds = list.remove(0); dataCount -= ds.getDataCount(); } diff --git a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/WrappedDataSet.java b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/WrappedDataSet.java index 2e315c6af..1a37e4f5a 100644 --- a/chartfx-dataset/src/main/java/de/gsi/dataset/spi/WrappedDataSet.java +++ b/chartfx-dataset/src/main/java/de/gsi/dataset/spi/WrappedDataSet.java @@ -1,11 +1,3 @@ -/***************************************************************************** - * * - * Chart Common - dataset wrapping another dataset * - * * - * modified: 2019-01-23 Harald Braeuning * - * * - ****************************************************************************/ - package de.gsi.dataset.spi; import java.util.ArrayList; @@ -97,6 +89,7 @@ public void setDataset(final DataSet dataset) { @Override public DataSet set(final DataSet other, final boolean copy) { - throw new UnsupportedOperationException("copy setting transposed data set is not implemented"); + lock().writeLockGuard(() -> other.lock().writeLockGuard(() -> this.setDataset(other))); + return fireInvalidated(new UpdatedDataEvent(this, "set(DataSet, boolean=" + copy + ")")); } } diff --git a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/CircularDoubleErrorDataSetTests.java b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/CircularDoubleErrorDataSetTests.java index e3e03ea74..5838b21dd 100644 --- a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/CircularDoubleErrorDataSetTests.java +++ b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/CircularDoubleErrorDataSetTests.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static de.gsi.dataset.DataSet.DIM_X; @@ -17,9 +18,9 @@ * * @author Alexander Krimm */ -public class CircularDoubleErrorDataSetTests { +class CircularDoubleErrorDataSetTests { @Test - public void defaultTests() { + void defaultTests() { CircularDoubleErrorDataSet dataSet = new CircularDoubleErrorDataSet("test", 5); assertEquals("test", dataSet.getName()); assertEquals(2, dataSet.getDimension()); @@ -32,8 +33,8 @@ public void defaultTests() { // add point dataSet.add(1.0, 2.0, 3.0, 2.2); assertEquals(1, dataSet.getDataCount()); - assertEquals(null, dataSet.getStyle(0)); - assertEquals(null, dataSet.getDataLabel(0)); + assertNull(dataSet.getStyle(0)); + assertNull(dataSet.getDataLabel(0)); assertEquals(1.0, dataSet.get(DIM_X, 0)); assertEquals(2.0, dataSet.get(DIM_Y, 0)); assertEquals(3.0, dataSet.getErrorNegative(DIM_Y, 0)); @@ -42,10 +43,10 @@ public void defaultTests() { assertEquals(0.0, dataSet.getErrorPositive(DIM_X, 0)); // add point with label - dataSet.add(1.1, 2.1, 3.1, 2.3, "testlabel"); + dataSet.add(1.1, 2.1, 3.1, 2.3, "testLabel"); assertEquals(2, dataSet.getDataCount()); - assertEquals("testlabel", dataSet.getDataLabel(1)); - assertEquals(null, dataSet.getStyle(1)); + assertEquals("testLabel", dataSet.getDataLabel(1)); + assertNull(dataSet.getStyle(1)); assertArrayEquals(new double[] { 1.0, 1.1 }, dataSet.getValues(DIM_X)); assertArrayEquals(new double[] { 2.0, 2.1 }, dataSet.getValues(DIM_Y)); assertArrayEquals(new double[] { 3.0, 3.1 }, dataSet.getErrorsNegative(DIM_Y)); @@ -54,9 +55,9 @@ public void defaultTests() { assertArrayEquals(new double[] { 0, 0 }, dataSet.getErrorsPositive(DIM_X)); // add point with label and style - dataSet.add(1.2, 2.2, 3.2, 2.4, "testlabel2", "color:red"); + dataSet.add(1.2, 2.2, 3.2, 2.4, "testLabel2", "color:red"); assertEquals(3, dataSet.getDataCount()); - assertEquals("testlabel2", dataSet.getDataLabel(2)); + assertEquals("testLabel2", dataSet.getDataLabel(2)); assertEquals("color:red", dataSet.getStyle(2)); // add points @@ -80,7 +81,7 @@ public void defaultTests() { } @Test - public void testThatAddingSingleValuesWillUpdateAxisDescriptionAccoringToNewValue() { + void testThatAddingSingleValuesWillUpdateAxisDescriptionAccordingToNewValue() { CircularDoubleErrorDataSet dataSet = new CircularDoubleErrorDataSet("test", 5); AxisDescription xAxisDescription = dataSet.getAxisDescription(DIM_X); AxisDescription yAxisDescription = dataSet.getAxisDescription(DIM_Y); @@ -105,7 +106,7 @@ public void testThatAddingSingleValuesWillUpdateAxisDescriptionAccoringToNewValu } @Test - public void testThatAddingMultipleValuesWillUpdateAxisDescriptionAccoringToNewValues() { + void testThatAddingMultipleValuesWillUpdateAxisDescriptionAccordingToNewValues() { CircularDoubleErrorDataSet dataSet = new CircularDoubleErrorDataSet("test", 5); AxisDescription xAxisDescription = dataSet.getAxisDescription(DIM_X); AxisDescription yAxisDescription = dataSet.getAxisDescription(DIM_Y); @@ -139,6 +140,36 @@ public void testThatAddingMultipleValuesWillUpdateAxisDescriptionAccoringToNewVa assertAxisDescriptionRange(yAxisDescription, -62., 44.); } + @Test + void testUpdateAxisRange() { + CircularDoubleErrorDataSet dataSet = new CircularDoubleErrorDataSet("test", 5); + AxisDescription xAxisDescription = dataSet.getAxisDescription(DIM_X); + AxisDescription yAxisDescription = dataSet.getAxisDescription(DIM_Y); + + assertEquals(Double.NaN, xAxisDescription.getMin()); + assertEquals(Double.NaN, xAxisDescription.getMax()); + assertEquals(Double.NaN, yAxisDescription.getMin()); + assertEquals(Double.NaN, yAxisDescription.getMax()); + + dataSet.add(1, 1, 0.1, 0.1); + assertEquals(1, xAxisDescription.getMin()); + assertEquals(1, xAxisDescription.getMax()); + assertEquals(0.9, yAxisDescription.getMin()); + assertEquals(1.1, yAxisDescription.getMax()); + + dataSet.add(2, 1, 0.1, 0.1); + assertEquals(1, xAxisDescription.getMin()); + assertEquals(2, xAxisDescription.getMax()); + assertEquals(0.9, yAxisDescription.getMin()); + assertEquals(1.1, yAxisDescription.getMax()); + + dataSet.add(2, 2, 0.1, 0.1); + assertEquals(1, xAxisDescription.getMin()); + assertEquals(2, xAxisDescription.getMax()); + assertEquals(0.9, yAxisDescription.getMin()); + assertEquals(2.1, yAxisDescription.getMax()); + } + private void assertAxisDescriptionRange(AxisDescription axisDescription, double min, double max) { assertEquals(min, axisDescription.getMin()); assertEquals(max, axisDescription.getMax()); diff --git a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/DataSetEqualityTests.java b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/DataSetEqualityTests.java index 5e3bbf29a..78a7fa7aa 100644 --- a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/DataSetEqualityTests.java +++ b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/DataSetEqualityTests.java @@ -23,49 +23,33 @@ * * @author rstein */ -public class DataSetEqualityTests { +class DataSetEqualityTests { @Test - public void testDataSetEquality() { + void testDataSetEquality() { // check for helper classes assertEquals(new DataRange(), new DataRange()); assertEquals(new DefaultAxisDescription(DIM_X, "default"), new DefaultAxisDescription(DIM_X, "default")); - // common DataSet implementations - assertEquals(new DoubleDataSet("default"), new DoubleDataSet("default")); - assertEquals(new DoubleErrorDataSet("default"), new DoubleErrorDataSet("default")); - assertEquals(new FloatDataSet("default"), new FloatDataSet("default")); - - assertEquals(new CircularDoubleErrorDataSet("default", 10), new CircularDoubleErrorDataSet("default", 11)); - - assertEquals(new DefaultDataSet("default"), new DefaultDataSet("default")); - assertEquals(new DefaultErrorDataSet("default"), new DefaultErrorDataSet("default")); - assertEquals(new DoubleDataSet("default"), new DoubleDataSet("default")); - assertEquals(new DoubleErrorDataSet("default"), new DoubleErrorDataSet("default")); - assertEquals(new FifoDoubleErrorDataSet("default", 10), new FifoDoubleErrorDataSet("default", 11)); - assertEquals(new FragmentedDataSet("default"), new FragmentedDataSet("default")); + // common DataSet implementations not covered by GenericDataSetTests assertEquals(new Histogram("default", 10, 0.0, 1.0, BINS_ALIGNED_WITH_BOUNDARY), new Histogram("default", 10, 0.0, 1.0, BINS_ALIGNED_WITH_BOUNDARY)); - // assertEquals(new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0), - // new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0)); + // assertEquals(new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0), new Histogram2("default", 10, 0.0, 1.0, 10, 0.0, 1.0)) assertEquals(new LabelledMarkerDataSet("default"), new LabelledMarkerDataSet("default")); - assertEquals(new LimitedIndexedTreeDataSet("default", 10), new LimitedIndexedTreeDataSet("default", 11)); - assertEquals(new RollingDataSet("default"), new RollingDataSet("default")); - assertEquals(new WrappedDataSet("default"), new WrappedDataSet("default")); - - assertEquals(new LimitedIndexedTreeDataSet("default", 100), new LimitedIndexedTreeDataSet("default", 100)); } /** * more specific test here DoubleErrorDataSet as stand-in for all AbstractDataSet derived classes */ @Test - public void testDoubleDataSetEquality() { + void testDoubleDataSetEquality() { // NOPMD NOSONAR number of asserts in method final DoubleErrorDataSet ds1 = new DoubleErrorDataSet("default"); final DoubleErrorDataSet ds2 = new DoubleErrorDataSet("default"); assertEquals(ds1, ds1); assertNotEquals(null, ds1); - assertNotEquals(ds1, new OneDimDataSet()); - assertNotEquals("", ds1); + //noinspection AssertBetweenInconvertibleTypes + assertNotEquals(ds1, new OneDimDataSet(), "incompatible class type"); // NOPMD NOSONAR + //noinspection AssertBetweenInconvertibleTypes + assertNotEquals("", ds1, "incompatible class type"); // NOPMD NOSONAR assertEquals(ds1, ds2); ds1.setName(null); diff --git a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericDataSetTests.java b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericDataSetTests.java new file mode 100644 index 000000000..72d98ba40 --- /dev/null +++ b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericDataSetTests.java @@ -0,0 +1,125 @@ +package de.gsi.dataset.spi; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import static de.gsi.dataset.DataSet.DIM_X; +import static de.gsi.dataset.DataSet.DIM_Y; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import de.gsi.dataset.DataSet; +import de.gsi.dataset.DataSetError; + +/** + * Generic DataSet interface tests that (nearly) all DataSets should fulfill. + * + * @author rstein + */ +class GenericDataSetTests { + private static final int DEFAULT_COUNT_MAX = 100; + private static final String DEFAULT_DATASET_NAME1 = "testDataSet1"; + private static final String DEFAULT_DATASET_NAME2 = "testDataSet1"; + + static Stream> dataSetClassProvider() { + return Stream.of( + AveragingDataSet.class, + CircularDoubleErrorDataSet.class, + DefaultDataSet.class, DefaultErrorDataSet.class, DoubleDataSet.class, DoubleErrorDataSet.class, + FifoDoubleErrorDataSet.class, FloatDataSet.class, FragmentedDataSet.class, + LimitedIndexedTreeDataSet.class, + MultiDimDoubleDataSet.class, + //RollingDataSet.class, + WrappedDataSet.class); + } + + @ParameterizedTest + @MethodSource("dataSetClassProvider") + @Timeout(1) + void testEquality(final Class clazz) throws AssertionError { + DataSet dataSet1 = getDefaultTestDataSet(clazz, DEFAULT_DATASET_NAME1, DEFAULT_COUNT_MAX + 2); + assertNotNull(dataSet1, "test data set: " + clazz.getName()); + assertEquals(DEFAULT_DATASET_NAME1, dataSet1.getName()); + DataSet dataSet2 = getDefaultTestDataSet(clazz, DEFAULT_DATASET_NAME2, DEFAULT_COUNT_MAX + 3); + assertNotNull(dataSet2, "test data set: " + clazz.getName()); + assertEquals(DEFAULT_DATASET_NAME2, dataSet2.getName()); + + assertEquals(dataSet1, dataSet2); + } + + @ParameterizedTest + @MethodSource("dataSetClassProvider") + @Timeout(1) + void testSetDataSet(final Class clazz) throws AssertionError { + DataSet dataSet = getDefaultTestDataSet(clazz, DEFAULT_DATASET_NAME1, DEFAULT_COUNT_MAX); + assertNotNull(dataSet, "test data set: " + clazz.getName()); + + DoubleDataSet testDataSet = new DoubleDataSet("DataSet"); + DoubleErrorDataSet testDataSetError = new DoubleErrorDataSet("DataSetError"); + final double error = (dataSet instanceof DataSetError) ? 0.1 : 0.0; + for (int i = 0; i < 10; i++) { + testDataSet.add(i, i); + testDataSetError.add(10 + i, 5 + i, error, error); + } + + for (int plane = 0; plane < 2; plane++) { + testDataSet.recomputeLimits(plane); + testDataSetError.recomputeLimits(plane); + } + + final AtomicInteger notifyCounter = new AtomicInteger(); + dataSet.addListener(evt -> notifyCounter.getAndIncrement()); + assertDoesNotThrow(() -> dataSet.set(testDataSet)); + assertSameDataRanges(testDataSet, dataSet); + dataSet.recomputeLimits(DIM_X); + dataSet.recomputeLimits(DIM_Y); + assertSameDataRanges(testDataSet, dataSet); + + assertEquals(1, notifyCounter.get()); + assertDoesNotThrow(() -> dataSet.set(testDataSet, true)); + assertEquals(2, notifyCounter.get()); + assertDoesNotThrow(() -> dataSet.set(testDataSet, false)); + assertEquals(3, notifyCounter.get()); + + notifyCounter.set(0); + assertDoesNotThrow(() -> dataSet.set(testDataSetError)); + assertSameDataRanges(testDataSetError, dataSet); + dataSet.recomputeLimits(DIM_X); + dataSet.recomputeLimits(DIM_Y); + assertSameDataRanges(testDataSetError, dataSet); + + assertEquals(1, notifyCounter.get()); + assertDoesNotThrow(() -> dataSet.set(testDataSetError, true)); + assertEquals(2, notifyCounter.get()); + assertDoesNotThrow(() -> dataSet.set(testDataSetError, false)); + assertEquals(3, notifyCounter.get()); + } + + public static void assertSameDataRanges(final DataSet reference, final DataSet test) throws AssertionError { + assertEquals(reference.getAxisDescription(DIM_X).getMin(), test.getAxisDescription(DIM_X).getMin()); + assertEquals(reference.getAxisDescription(DIM_X).getMax(), test.getAxisDescription(DIM_X).getMax()); + assertEquals(reference.getAxisDescription(DIM_Y).getMin(), test.getAxisDescription(DIM_Y).getMin()); + assertEquals(reference.getAxisDescription(DIM_Y).getMax(), test.getAxisDescription(DIM_Y).getMax()); + } + + public static DataSet getDefaultTestDataSet(final Class clazz, final String dataSetName, final int initialCapacity) throws AssertionError { + try { + return Objects.requireNonNull(clazz, "clazz must not be null").getDeclaredConstructor(String.class).newInstance(dataSetName); + } catch (Exception t) { // NOPMD NOSONAR + // re-try with alternate initialisation + try { + return Objects.requireNonNull(clazz, "clazz must not be null").getDeclaredConstructor(String.class, int.class).newInstance(dataSetName, initialCapacity); + } catch (Exception e) { // NOPMD NOSONAR + // aggregate different types and sterilise to 'Exception' + throw new AssertionError("exception while constructing", e); + } + } + } +} diff --git a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericNotifyTests.java b/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericNotifyTests.java deleted file mode 100644 index fa1fef3ec..000000000 --- a/chartfx-dataset/src/test/java/de/gsi/dataset/spi/GenericNotifyTests.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.gsi.dataset.spi; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.jupiter.api.Test; - -import de.gsi.dataset.DataSet; - -public class GenericNotifyTests { - @Test - void notifyTests() { // NOPMD - testDataSetSetterNotify(DoubleDataSet.class); - testDataSetSetterNotify(DoubleErrorDataSet.class); - testDataSetSetterNotify(FloatDataSet.class); - testDataSetSetterNotify(LimitedIndexedTreeDataSet.class); - testDataSetSetterNotify(MultiDimDoubleDataSet.class); - } - - void testDataSetSetterNotify(Class testDataSetClass) throws AssertionError { - try { - final DataSet testDataSet = testDataSetClass.getDeclaredConstructor(String.class).newInstance("test - " + testDataSetClass.getName()); - final AtomicInteger counter = new AtomicInteger(); - testDataSet.addListener(evt -> counter.getAndIncrement()); - testDataSet.set(new DoubleErrorDataSet("null dummy")); - assertEquals(1, counter.get()); - testDataSet.set(new DoubleErrorDataSet("null dummy"), true); - assertEquals(2, counter.get()); - testDataSet.set(new DoubleErrorDataSet("null dummy"), false); - assertEquals(3, counter.get()); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { - throw new AssertionError("caught exception for " + testDataSetClass.getName(), e); - } - } -}