package org.djutils.data; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.djutils.immutablecollections.ImmutableList; import org.djutils.immutablecollections.ImmutableMap; import org.djutils.primitives.Primitive; import org.junit.Test; /** * TestListTable tests the functions of the ListTable, the Column and the Record.
*
* Copyright (c) 2020-2021 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See * for project information https://djutils.org. The DJUTILS project is * distributed under a three-clause BSD-style license, which can be found at * https://djutils.org/docs/license.html.
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel */ public class TestListDataTable { /** Test the ListTable, the Column and the Record. */ @Test public void testListTable() { DataColumn column1 = new SimpleDataColumn<>("time", "time rounded to second [s]", int.class); DataColumn column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class); DataColumn column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class); assertEquals("time", column1.getId()); assertEquals("time rounded to second [s]", column1.getDescription()); assertEquals(int.class, column1.getValueType()); List> columns = new ArrayList<>(); columns.add(column1); columns.add(column2); columns.add(column3); ListDataTable table = new ListDataTable("tableId", "tableDescription", columns); assertEquals("tableId", table.getId()); assertEquals("tableDescription", table.getDescription()); assertEquals(3, table.getColumns().size()); assertArrayEquals(new String[] {"time", "value", "remark"}, table.getColumnIds()); assertArrayEquals(new String[] {"time rounded to second [s]", "measured value [m]", "remark about the measurement"}, table.getColumnDescriptions()); assertArrayEquals(new Class[] {int.class, double.class, String.class}, table.getColumnDataTypes()); assertArrayEquals(new String[] {"int", "double", "java.lang.String"}, table.getColumnDataTypeStrings()); // add some data assertTrue(table.isEmpty()); table.addRecord(new Object[] {2, 5.0, "normal"}); assertFalse(table.isEmpty()); Map, Object> cdata = new LinkedHashMap<>(); cdata.put(column3, "second"); cdata.put(column2, 7.7); cdata.put(column1, 4); table.addRecordByColumns(cdata); Map idata = new TreeMap<>(); idata.put("time", 6); idata.put("value", 9.12); idata.put("remark", "third"); table.addRecordByColumnIds(idata); DataRecord record = table.iterator().next(); assertArrayEquals(new Object[] {2, 5.0, "normal"}, record.getValues()); DataColumn c1 = new SimpleDataColumn<>("x", "x", double.class); DataColumn c2 = new SimpleDataColumn<>("y", "y", double[][].class); ListDataTable txy = new ListDataTable("xy[][]", "x, y[][]", ImmutableList.of(c1, c2)); assertArrayEquals(new String[] {"x", "y"}, txy.getColumnIds()); assertArrayEquals(new String[] {"x", "y"}, txy.getColumnDescriptions()); assertArrayEquals(new Class[] {double.class, double[][].class}, txy.getColumnDataTypes()); assertArrayEquals(new String[] {"double", "[[D"}, txy.getColumnDataTypeStrings()); String tableString = table.toString(); assertTrue(tableString.startsWith("ListDataTable")); assertTrue(tableString.contains("tableId")); assertTrue(tableString.contains("tableDescription")); assertTrue(tableString.contains("SimpleDataColumn")); String recordString = table.iterator().next().toString(); assertTrue(recordString.startsWith("ListDataTable.ListRecord")); assertTrue(recordString.contains("time = 2")); assertTrue(recordString.contains("value = 5.0")); } /** Test the ListTable, the Column and the Record. */ @Test public void testSubclassListTable() { // table with double column DataColumn column = new SimpleDataColumn<>("value", "measured value", double.class); List> columns = new ArrayList<>(); columns.add(column); ListDataTable table = new ListDataTable("tableId", "tableDescription", columns); // try double arguments in double column table.addRecord(new Object[] {5.0}); Map, Object> cdata = new LinkedHashMap<>(); cdata.put(column, 7.7); table.addRecordByColumns(cdata); Map idata = new TreeMap<>(); idata.put("value", 9.12); table.addRecordByColumnIds(idata); // try Double arguments in double column ListDataTable table2 = new ListDataTable("tableId", "tableDescription", columns); table2.addRecord(new Object[] {Double.valueOf(5.0)}); cdata = new LinkedHashMap<>(); cdata.put(column, Double.valueOf(7.7)); table2.addRecordByColumns(cdata); idata = new TreeMap<>(); idata.put("value", Double.valueOf(9.12)); table2.addRecordByColumnIds(idata); // compare both tables tableCompare(table, table2); // table with Number column DataColumn nColumn = new SimpleDataColumn<>("value", "measured value", Number.class); List> nColumns = new ArrayList<>(); nColumns.add(nColumn); ListDataTable nTable = new ListDataTable("tableId", "tableDescription", nColumns); // try double arguments in Number column nTable.addRecord(new Object[] {5.0}); Map, Object> cndata = new LinkedHashMap<>(); cndata.put(nColumn, 7.7); nTable.addRecordByColumns(cndata); Map indata = new TreeMap<>(); indata.put("value", 9.12); nTable.addRecordByColumnIds(indata); // try Double arguments in Number column ListDataTable nTable2 = new ListDataTable("tableId", "tableDescription", nColumns); nTable2.addRecord(new Object[] {Double.valueOf(5.0)}); cndata = new LinkedHashMap<>(); cndata.put(nColumn, Double.valueOf(7.7)); nTable2.addRecordByColumns(cndata); indata = new TreeMap<>(); indata.put("value", Double.valueOf(9.12)); nTable2.addRecordByColumnIds(indata); // compare both tables tableCompare(nTable, nTable2); } /** * Compare the contents of two tables, where primitive content and wrapped primitive content (e.g., a double and a Double) * are considered the same if the stored value is the same. * @param table1 the first table * @param table2 the second table */ public static void tableCompare(final ListDataTable table1, final ListDataTable table2) { assertEquals(table1.getColumns().size(), table2.getColumns().size()); assertEquals(table1.getId(), table2.getId()); assertEquals(table1.getDescription(), table2.getDescription()); assertEquals(table1.getNumberOfColumns(), table2.getNumberOfColumns()); assertEquals(table1.getColumns().size(), table2.getColumns().size()); for (int i = 0; i < table1.getColumns().size(); i++) { DataColumn c1 = table1.getColumns().get(i); DataColumn c2 = table2.getColumns().get(i); assertEquals(c1.getId(), c2.getId()); assertEquals(c1.getDescription(), c2.getDescription()); assertTrue(Primitive.isPrimitiveAssignableFrom(c1.getValueType(), c2.getValueType())); assertTrue(Primitive.isPrimitiveAssignableFrom(c2.getValueType(), c1.getValueType())); } Iterator it2 = table2.iterator(); for (DataRecord r1 : table1) { assertTrue(it2.hasNext()); DataRecord r2 = it2.next(); for (int i = 0; i < table1.getColumns().size(); i++) { DataColumn c1 = table1.getColumns().get(i); String c2id = table2.getColumns().get(i).getId(); Object v1 = r1.getValue(c1); Object v2 = r2.getValue(c2id); assertEquals(v1.toString(), v2.toString()); } } assertFalse(it2.hasNext()); } /** Test column and table construction with wrong arguments. */ @Test public void testIllegalColumnTable() { DataColumn column1 = new SimpleDataColumn<>("time", "time, rounded to second [s]", int.class); DataColumn column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class); DataColumn column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class); List> columns = new ArrayList<>(); columns.add(column1); columns.add(column2); columns.add(column3); // // test illegal columns // try { new SimpleDataColumn<>(null, "measured value [m]", double.class); fail("null id should have thrown NullPointerException"); } catch (NullPointerException npe) { // ok } try { new SimpleDataColumn<>("value", null, double.class); fail("null description should have thrown NullPointerException"); } catch (NullPointerException npe) { // ok } try { new SimpleDataColumn<>("value", "measured value [m]", null); fail("null valueType should have thrown NullPointerException"); } catch (NullPointerException npe) { // ok } try { new SimpleDataColumn<>("", "measured value [m]", double.class); fail("empty id should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { // ok } // // test illegal tables // List> cx = new ArrayList<>(); cx.add(column1); cx.add(column1); // duplicate cx.add(column3); try { new ListDataTable("tableId", "tableDescription", cx); fail("duplicate column should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { // ok } cx = new ArrayList<>(); cx.add(column1); cx.add(new SimpleDataColumn<>("time", "another timestamp", double.class)); cx.add(column3); try { new ListDataTable("tableId", "tableDescription", cx); fail("duplicate column id should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { // ok } try { new ListDataTable(null, "tableDescription", columns); fail("null id should have thrown NullPointerException"); } catch (NullPointerException iae) { // ok } try { new ListDataTable("", "tableDescription", columns); fail("empty id should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { // ok } try { new ListDataTable("tableId", null, columns); fail("null id should have thrown NullPointerException"); } catch (NullPointerException iae) { // ok } try { new ListDataTable("tableId", "tableDescription", (ImmutableList>) null); fail("null columns should have thrown NullPointerException"); } catch (NullPointerException iae) { // ok } try { new ListDataTable("tableId", "tableDescription", (Collection>) null); fail("null columns should have thrown NullPointerException"); } catch (NullPointerException iae) { // ok } try { new ListDataTable("tableId", "tableDescription", new ArrayList>()); fail("zero columns should have thrown IllegalArgumentException"); } catch (IllegalArgumentException iae) { // ok } } /** Test column and table construction with wrong value arguments. */ @Test public void testIllegalRecordTable() { DataColumn column1 = new SimpleDataColumn<>("time", "time, rounded to second [s]", int.class); DataColumn column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class); DataColumn column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class); List> columns = new ArrayList<>(); columns.add(column1); columns.add(column2); columns.add(column3); ListDataTable table = new ListDataTable("tableId", "tableDescription", columns); // // test null data // try { table.addRecord((Object[]) null); fail("null data record should have raised exception"); } catch (NullPointerException npe) { // ok } try { table.addRecordByColumnIds((Map) null); fail("null data record should have raised exception"); } catch (NullPointerException npe) { // ok } try { table.addRecordByColumns((Map, Object>) null); fail("null data record should have raised exception"); } catch (NullPointerException npe) { // ok } // // test too few columns data // try { table.addRecord(new Object[] {}); fail("empty data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { table.addRecordByColumnIds(new HashMap()); fail("empty data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { table.addRecordByColumns(new HashMap, Object>()); fail("empty data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } // // test too many columns data // try { table.addRecord(new Object[] {1, 2, 3, 4}); fail("too long data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { table.addRecordByColumnIds(ImmutableMap.of("time", 3, "value", 3.5, "remark", "none", "extra", "xx")); fail("too long data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { DataColumn column4 = new SimpleDataColumn<>("c4", "column 4", String.class); table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 3.5, column3, "remark", column4, "xx")); fail("too long data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } // // test wrong type data // try { table.addRecord(new Object[] {1, 2, 3}); fail("wrong type data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { table.addRecordByColumnIds(ImmutableMap.of("time", 3, "value", 2L, "remark", "none")); fail("wrong type data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 3.5, column3, 2L)); fail("wrong type data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } // // test missing column data, where the number of columns is okay // try { table.addRecordByColumnIds(ImmutableMap.of("time", 3, "remark", "none", "wrong", 3)); fail("wrong type data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } try { DataColumn column4 = new SimpleDataColumn<>("c4", "column 4", String.class); table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 4.5, column4, "xx")); fail("wrong type data record should have raised exception"); } catch (IllegalArgumentException iae) { // ok } } }