From c06ed77fbe5a78985b42f1873f11dd9d6dd57a78 Mon Sep 17 00:00:00 2001 From: tpylak <tpylak> Date: Fri, 23 Jul 2010 14:10:44 +0000 Subject: [PATCH] LMS-1652 bugfix: create feature vector matrix taking the plate geometry into account, not the wells for which values have been provided SVN: 17178 --- .../migration/MigrationStepFrom003To004.java | 50 +++++++++++++------ .../CsvFeatureVectorMigrator.java | 4 +- .../CsvToCanonicalFeatureVector.java | 30 ++++++++--- .../FeatureVectorStorageProcessor.java | 8 +-- .../generic/server/FeatureTableBuilder.java | 47 ++++++++++------- .../dss/generic/server/FeatureTableRow.java | 6 +-- ...mageAnalysisMergedRowsReportingPlugin.java | 6 +-- .../shared/api/v1/dto/FeatureVector.java | 6 +-- .../shared/dto/PlateFeatureValues.java | 3 ++ .../CsvToCanonicalFeatureVectorTest.java | 2 +- .../server/DssServiceRpcScreeningTest.java | 16 +++--- 11 files changed, 115 insertions(+), 63 deletions(-) diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/migration/MigrationStepFrom003To004.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/migration/MigrationStepFrom003To004.java index 9cbfd37fa08..cc20a610e6d 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/migration/MigrationStepFrom003To004.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/migration/MigrationStepFrom003To004.java @@ -93,7 +93,10 @@ public class MigrationStepFrom003To004 implements IMigrationStep { long id = rs.getLong("id"); String permId = rs.getString("perm_id"); - return new MigrationDatasetRef(id, permId); + int plateWidth = rs.getInt("plate_width"); + int plateHeight = rs.getInt("plate_height"); + return new MigrationDatasetRef(id, permId, plateWidth, plateHeight); + } }; @@ -103,10 +106,16 @@ public class MigrationStepFrom003To004 implements IMigrationStep final private String permId; - public MigrationDatasetRef(long id, String permId) + final private int plateWidth; + + final private int plateHeight; + + public MigrationDatasetRef(long id, String permId, int plateWidth, int plateHeight) { this.id = id; this.permId = permId; + this.plateWidth = plateWidth; + this.plateHeight = plateHeight; } public long getId() @@ -118,6 +127,16 @@ public class MigrationStepFrom003To004 implements IMigrationStep { return permId; } + + public int getPlateWidth() + { + return plateWidth; + } + + public int getPlateHeight() + { + return plateHeight; + } } public void performPostMigration(SimpleJdbcTemplate jdbc, DataSource dataSource) @@ -149,13 +168,13 @@ public class MigrationStepFrom003To004 implements IMigrationStep boolean wholeMigrationOk = true; for (Entry<MigrationDatasetRef, DatasetFileLines> entry : fileMap.entrySet()) { - long datasetId = entry.getKey().getId(); - String permId = entry.getKey().getPermId(); + MigrationDatasetRef datasetRef = entry.getKey(); + String permId = datasetRef.getPermId(); DatasetFileLines featureVectorLines = entry.getValue(); try { operationLog.info("Migrating dataset: " + permId); - migrateDataset(jdbc, dao, datasetId, featureVectorLines); + migrateDataset(jdbc, dao, datasetRef, featureVectorLines); } catch (Exception ex) { operationLog.error("Cannot migrate dataset " + permId + ": " + ex.getMessage()); @@ -169,10 +188,11 @@ public class MigrationStepFrom003To004 implements IMigrationStep return wholeMigrationOk; } - private void migrateDataset(SimpleJdbcTemplate jdbc, IImagingQueryDAO dao, long datasetId, - DatasetFileLines featureVectorLines) + private void migrateDataset(SimpleJdbcTemplate jdbc, IImagingQueryDAO dao, + MigrationDatasetRef datasetRef, DatasetFileLines featureVectorLines) { - List<CanonicalFeatureVector> fvecs = extractFeatureVectors(featureVectorLines); + long datasetId = datasetRef.getId(); + List<CanonicalFeatureVector> fvecs = extractFeatureVectors(featureVectorLines, datasetRef); int deleted = deleteFeatureVectors(datasetId, jdbc); if (deleted != fvecs.size()) { @@ -189,11 +209,13 @@ public class MigrationStepFrom003To004 implements IMigrationStep FeatureVectorUploader.uploadFeatureVectors(dao, fvecs, datasetId); } - private List<CanonicalFeatureVector> extractFeatureVectors(DatasetFileLines featureVectorLines) + private List<CanonicalFeatureVector> extractFeatureVectors(DatasetFileLines featureVectorLines, + MigrationDatasetRef datasetRef) { CsvToCanonicalFeatureVectorConfiguration convertorConfig = createCsvConfig(); - return new CsvToCanonicalFeatureVector(featureVectorLines, convertorConfig).convert(); + return new CsvToCanonicalFeatureVector(featureVectorLines, convertorConfig, datasetRef + .getPlateHeight(), datasetRef.getPlateWidth()).convert(); } private static DatasetFileLines getDatasetFileLines(File file, final char separator) @@ -217,10 +239,10 @@ public class MigrationStepFrom003To004 implements IMigrationStep private List<MigrationDatasetRef> fetchImagingDatasets(SimpleJdbcTemplate simpleJdbcTemplate) { - return simpleJdbcTemplate - .query( - "select distinct d.id, d.perm_id from feature_defs defs, data_sets d where d.id = defs.ds_id", - DATASET_ROW_MAPPER); + return simpleJdbcTemplate.query( + "select distinct d.id, d.perm_id, c.spots_width plate_width, c.spots_height plate_height " + + " from feature_defs defs, data_sets d, containers c " + + " where d.id = defs.ds_id and c.id = d.cont_id;", DATASET_ROW_MAPPER); } private String tryGetDatabaseInstanceUUID(File storeRootDir) diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvFeatureVectorMigrator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvFeatureVectorMigrator.java index 7c2135e12a7..617bc9eed20 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvFeatureVectorMigrator.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvFeatureVectorMigrator.java @@ -138,7 +138,9 @@ public class CsvFeatureVectorMigrator extends AbstractFeatureVectorMigrator { fileLines = getDatasetFileLines(fileToMigrate); CsvToCanonicalFeatureVector convertor = - new CsvToCanonicalFeatureVector(fileLines, convertorConfig); + new CsvToCanonicalFeatureVector(fileLines, convertorConfig, + screeningDataSetInfo.getContainerRows(), screeningDataSetInfo + .getContainerColumns()); ArrayList<CanonicalFeatureVector> fvecs = convertor.convert(); FeatureVectorUploader uploader = diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVector.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVector.java index 9f023385fc4..a65192ef752 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVector.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVector.java @@ -80,16 +80,22 @@ public class CsvToCanonicalFeatureVector private int yColumn = -1; - private int maxRow = 0; + private int maxRowFound = 0; - private int maxCol = 0; + private int maxColFound = 0; + + private final int maxPlateGeometryRow; + + private final int maxPlateGeometryCol; public CsvToCanonicalFeatureVector(DatasetFileLines fileLines, - CsvToCanonicalFeatureVectorConfiguration config) + CsvToCanonicalFeatureVectorConfiguration config, int maxRow, int maxCol) { this.configuration = config; this.header = fileLines.getHeaderTokens(); this.lines = fileLines.getDataLines(); + this.maxPlateGeometryRow = maxRow; + this.maxPlateGeometryCol = maxCol; } public ArrayList<CanonicalFeatureVector> convert() @@ -102,7 +108,8 @@ public class CsvToCanonicalFeatureVector private ArrayList<CanonicalFeatureVector> convertColumnsToFeatureVectors() { - final Geometry geometry = Geometry.createFromRowColDimensions(maxRow, maxCol); + final Geometry geometry = + Geometry.createFromRowColDimensions(maxPlateGeometryRow, maxPlateGeometryCol); ArrayList<CanonicalFeatureVector> result = new ArrayList<CanonicalFeatureVector>(); for (FeatureColumn column : columns) @@ -147,6 +154,13 @@ public class CsvToCanonicalFeatureVector { readLine(line); } + if (maxColFound > maxPlateGeometryCol || maxRowFound > maxPlateGeometryRow) + { + throw new IllegalStateException(String.format( + "Feature vector has values outside the plate geometry. " + + "Plate geometry: (%d, %d), well: (%d, %d).", maxPlateGeometryRow, + maxPlateGeometryCol, maxRowFound, maxColFound)); + } } private void readLine(String[] line) @@ -168,14 +182,14 @@ public class CsvToCanonicalFeatureVector } } - if (well.getRow() > maxRow) + if (well.getRow() > maxRowFound) { - maxRow = well.getRow(); + maxRowFound = well.getRow(); } - if (well.getColumn() > maxCol) + if (well.getColumn() > maxColFound) { - maxCol = well.getColumn(); + maxColFound = well.getColumn(); } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/FeatureVectorStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/FeatureVectorStorageProcessor.java index e39cb3642cc..fd0de873701 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/FeatureVectorStorageProcessor.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/FeatureVectorStorageProcessor.java @@ -99,15 +99,15 @@ public class FeatureVectorStorageProcessor extends AbstractDelegatingStorageProc private void loadDataSetIntoDatabase(File dataSet, DataSetInformation dataSetInformation) throws IOException { + ScreeningContainerDatasetInfo datasetInfo = createScreeningDatasetInfo(dataSetInformation); DatasetFileLines fileLines = getDatasetFileLines(dataSet); CsvToCanonicalFeatureVector convertor = - new CsvToCanonicalFeatureVector(fileLines, convertorConfig); + new CsvToCanonicalFeatureVector(fileLines, convertorConfig, datasetInfo + .getContainerRows(), datasetInfo.getContainerColumns()); List<CanonicalFeatureVector> fvecs = convertor.convert(); dataAccessObject = createDAO(); - FeatureVectorUploader uploader = - new FeatureVectorUploader(dataAccessObject, - createScreeningDatasetInfo(dataSetInformation)); + FeatureVectorUploader uploader = new FeatureVectorUploader(dataAccessObject, datasetInfo); uploader.uploadFeatureVectors(fvecs); } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java index 121b2f3a4bc..c241686e773 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java @@ -17,6 +17,7 @@ package ch.systemsx.cisd.openbis.dss.generic.server; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -48,13 +49,18 @@ public class FeatureTableBuilder private static final class Bundle { private ImgDatasetDTO dataSet; + private Map<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> featureDefToValuesMap; } - + private final IImagingQueryDAO dao; + private final IEncapsulatedOpenBISService service; + private final List<Bundle> bundles; + private final Map<String, Integer> featureNameToIndexMap; + private final Set<String> featureNames; /** @@ -62,15 +68,16 @@ public class FeatureTableBuilder */ public FeatureTableBuilder(IImagingQueryDAO dao, IEncapsulatedOpenBISService service) { - this(Collections.<String>emptyList(), dao, service); + this(Collections.<String> emptyList(), dao, service); } - + /** * Creates an instance for specified DAO and openBIS service but filters on specified features. * * @param featureNames And empty list means no filtering. */ - public FeatureTableBuilder(List<String> featureNames, IImagingQueryDAO dao, IEncapsulatedOpenBISService service) + public FeatureTableBuilder(List<String> featureNames, IImagingQueryDAO dao, + IEncapsulatedOpenBISService service) { this.dao = dao; this.service = service; @@ -78,7 +85,7 @@ public class FeatureTableBuilder featureNameToIndexMap = new LinkedHashMap<String, Integer>(); this.featureNames = new HashSet<String>(featureNames); } - + /** * Adds feature vectors for specified data set. */ @@ -101,21 +108,21 @@ public class FeatureTableBuilder { if (featureNameToIndexMap.containsKey(featureName) == false) { - featureNameToIndexMap.put(featureName, new Integer(featureNameToIndexMap.size())); + featureNameToIndexMap.put(featureName, + new Integer(featureNameToIndexMap.size())); } List<ImgFeatureValuesDTO> featureValueSets = - dao.getFeatureValues(featureDefinition); + dao.getFeatureValues(featureDefinition); if (featureValueSets.isEmpty()) { throw new UserFailureException("At least one set of values for feature " - + featureName + " of data set " + dataSetCode - + " expected."); + + featureName + " of data set " + dataSetCode + " expected."); } bundle.featureDefToValuesMap.put(featureDefinition, featureValueSets); } } } - + /** * Returns all feature names found. If the feature name list in the constructor is not empty the * result will a subset of this list. @@ -124,7 +131,7 @@ public class FeatureTableBuilder { return new ArrayList<String>(featureNameToIndexMap.keySet()); } - + /** * Returns all features per well coordinates. */ @@ -146,20 +153,24 @@ public class FeatureTableBuilder row.setPlateIdentifier(identifier); row.setRowIndex(rowIndex); row.setColumnIndex(colIndex); - double[] valueArray = new double[featureNameToIndexMap.size()]; - for (int i = 0; i < valueArray.length; i++) - { - valueArray[i] = Double.NaN; - } - for (Entry<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> entry : bundle.featureDefToValuesMap.entrySet()) + float[] valueArray = new float[featureNameToIndexMap.size()]; + Arrays.fill(valueArray, Float.NaN); + for (Entry<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> entry : bundle.featureDefToValuesMap + .entrySet()) { ImgFeatureDefDTO featureDefinition = entry.getKey(); List<ImgFeatureValuesDTO> featureValueSets = entry.getValue(); // We take only the first set of feature value sets ImgFeatureValuesDTO featureValueDTO = featureValueSets.get(0); PlateFeatureValues featureValues = featureValueDTO.getValues(); + if (rowIndex > featureValues.getGeometry().getHeight() + || colIndex > featureValues.getGeometry().getWidth()) + { + break; + } Integer index = featureNameToIndexMap.get(featureDefinition.getName()); - assert index != null : "No index for feature " + featureDefinition.getName(); + assert index != null : "No index for feature " + + featureDefinition.getName(); valueArray[index] = featureValues.getForWellLocation(rowIndex, colIndex); } row.setFeatureValues(valueArray); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java index 92de1bb02e0..37c31a8b579 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java @@ -35,7 +35,7 @@ public class FeatureTableRow private int columnIndex; - private double[] featureValues; + private float[] featureValues; public final String getDataSetCode() { @@ -77,12 +77,12 @@ public class FeatureTableRow this.columnIndex = columnIndex; } - public final double[] getFeatureValues() + public final float[] getFeatureValues() { return featureValues; } - public final void setFeatureValues(double[] featureValues) + public final void setFeatureValues(float[] featureValues) { this.featureValues = featureValues; } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java index 9bc716c1bcf..532a4194888 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java @@ -99,11 +99,11 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu values.add(new StringTableCell(row.getPlateIdentifier().toString())); values.add(new StringTableCell(PlateUtils.translateRowNumberIntoLetterCode(row.getRowIndex()))); values.add(new IntegerTableCell(row.getColumnIndex())); - double[] featureValues = row.getFeatureValues(); + float[] featureValues = row.getFeatureValues(); StringTableCell nullValue = new StringTableCell(""); - for (double value : featureValues) + for (float value : featureValues) { - if (Double.isNaN(value)) + if (Float.isNaN(value)) { values.add(nullValue); } else diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVector.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVector.java index a68a1a0b5a3..9bd819cde7d 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVector.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVector.java @@ -14,9 +14,9 @@ public class FeatureVector implements Serializable private final WellPosition wellPosition; - private final double[] values; + private final float[] values; - public FeatureVector(WellPosition well, double[] values) + public FeatureVector(WellPosition well, float[] values) { this.wellPosition = well; this.values = values; @@ -29,7 +29,7 @@ public class FeatureVector implements Serializable } /** feature vector values */ - public double[] getValues() + public float[] getValues() { return values; } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/dto/PlateFeatureValues.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/dto/PlateFeatureValues.java index 8916463b734..69e05aa72f9 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/dto/PlateFeatureValues.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/dto/PlateFeatureValues.java @@ -16,6 +16,8 @@ package ch.systemsx.cisd.openbis.plugin.screening.shared.dto; +import java.util.Arrays; + import ch.systemsx.cisd.base.convert.NativeTaggedArray; import ch.systemsx.cisd.base.mdarray.MDFloatArray; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Geometry; @@ -50,6 +52,7 @@ public final class PlateFeatureValues { this.geometry = geometry; this.valueArray = new MDFloatArray(geometry.getCartesianDimensions()); + Arrays.fill(valueArray.getAsFlatArray(), Float.NaN); } /** diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVectorTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVectorTest.java index 9b1201514e1..9575906ecd1 100644 --- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVectorTest.java +++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/featurevector/CsvToCanonicalFeatureVectorTest.java @@ -47,7 +47,7 @@ public class CsvToCanonicalFeatureVectorTest extends AssertJUnit CsvToCanonicalFeatureVectorConfiguration config = new CsvToCanonicalFeatureVectorConfiguration("WellName", "WellName"); CsvToCanonicalFeatureVector converter = - new CsvToCanonicalFeatureVector(getDatasetFileLines(), config); + new CsvToCanonicalFeatureVector(getDatasetFileLines(), config, 16, 24); ArrayList<CanonicalFeatureVector> fvs = converter.convert(); // Not all the columns contain numerical data assertEquals(16, fvs.size()); diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningTest.java index 6ef04417ef3..e05b46da9a8 100644 --- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningTest.java +++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreeningTest.java @@ -128,20 +128,20 @@ public class DssServiceRpcScreeningTest extends AssertJUnit assertSame(r1, dataSets.get(0).getDataset()); assertEquals("[f1, f2]", dataSets.get(0).getFeatureNames().toString()); - assertFeatureVector(1, 1, dataSets.get(0).getFeatureVectors().get(0), 244.5, 245.5); - assertFeatureVector(1, 2, dataSets.get(0).getFeatureVectors().get(1), 242.25, 243.25); + assertFeatureVector(1, 1, dataSets.get(0).getFeatureVectors().get(0), 244.5f, 245.5f); + assertFeatureVector(1, 2, dataSets.get(0).getFeatureVectors().get(1), 242.25f, 243.25f); assertEquals(2, dataSets.get(0).getFeatureVectors().size()); assertSame(r2, dataSets.get(1).getDataset()); assertEquals("[f2]", dataSets.get(1).getFeatureNames().toString()); - assertFeatureVector(1, 1, dataSets.get(1).getFeatureVectors().get(0), 249.0); - assertFeatureVector(1, 2, dataSets.get(1).getFeatureVectors().get(1), 244.5); + assertFeatureVector(1, 1, dataSets.get(1).getFeatureVectors().get(0), 249.0f); + assertFeatureVector(1, 2, dataSets.get(1).getFeatureVectors().get(1), 244.5f); assertEquals(2, dataSets.get(1).getFeatureVectors().size()); assertEquals(2, dataSets.size()); context.assertIsSatisfied(); } private void assertFeatureVector(int expectedRowNumber, int expectedColumnNumber, - FeatureVector featureVector, double... expectedValues) + FeatureVector featureVector, float... expectedValues) { assertEquals(expectedRowNumber, featureVector.getWellPosition().getWellRow()); assertEquals(expectedColumnNumber, featureVector.getWellPosition().getWellColumn()); @@ -149,10 +149,10 @@ public class DssServiceRpcScreeningTest extends AssertJUnit assertEquals(asList(expectedValues), asList(featureVector.getValues())); } - private List<Double> asList(double[] values) + private List<Float> asList(float[] values) { - List<Double> list = new ArrayList<Double>(); - for (double value : values) + List<Float> list = new ArrayList<Float>(); + for (float value : values) { list.add(value); } -- GitLab