View Javadoc
1   package org.djutils.data;
2   
3   import static org.junit.Assert.assertArrayEquals;
4   import static org.junit.Assert.assertEquals;
5   import static org.junit.Assert.assertFalse;
6   import static org.junit.Assert.assertTrue;
7   import static org.junit.Assert.fail;
8   
9   import java.util.ArrayList;
10  import java.util.Collection;
11  import java.util.HashMap;
12  import java.util.Iterator;
13  import java.util.LinkedHashMap;
14  import java.util.List;
15  import java.util.Map;
16  import java.util.TreeMap;
17  
18  import org.djutils.immutablecollections.ImmutableList;
19  import org.djutils.immutablecollections.ImmutableMap;
20  import org.djutils.primitives.Primitive;
21  import org.junit.Test;
22  
23  /**
24   * TestListTable tests the functions of the ListTable, the Column and the Record. <br>
25   * <br>
26   * Copyright (c) 2020-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
27   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
28   * distributed under a three-clause BSD-style license, which can be found at
29   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
30   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
33   */
34  public class TestListDataTable
35  {
36      /** Test the ListTable, the Column and the Record. */
37      @Test
38      public void testListTable()
39      {
40          DataColumn<Integer> column1 = new SimpleDataColumn<>("time", "time rounded to second [s]", int.class);
41          DataColumn<Double> column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class);
42          DataColumn<String> column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class);
43  
44          assertEquals("time", column1.getId());
45          assertEquals("time rounded to second [s]", column1.getDescription());
46          assertEquals(int.class, column1.getValueType());
47  
48          List<DataColumn<?>> columns = new ArrayList<>();
49          columns.add(column1);
50          columns.add(column2);
51          columns.add(column3);
52          ListDataTable table = new ListDataTable("tableId", "tableDescription", columns);
53  
54          assertEquals("tableId", table.getId());
55          assertEquals("tableDescription", table.getDescription());
56          assertEquals(3, table.getColumns().size());
57  
58          assertArrayEquals(new String[] {"time", "value", "remark"}, table.getColumnIds());
59          assertArrayEquals(new String[] {"time rounded to second [s]", "measured value [m]", "remark about the measurement"},
60                  table.getColumnDescriptions());
61          assertArrayEquals(new Class<?>[] {int.class, double.class, String.class}, table.getColumnDataTypes());
62          assertArrayEquals(new String[] {"int", "double", "java.lang.String"}, table.getColumnDataTypeStrings());
63  
64          // add some data
65          assertTrue(table.isEmpty());
66          table.addRecord(new Object[] {2, 5.0, "normal"});
67          assertFalse(table.isEmpty());
68          Map<DataColumn<?>, Object> cdata = new LinkedHashMap<>();
69          cdata.put(column3, "second");
70          cdata.put(column2, 7.7);
71          cdata.put(column1, 4);
72          table.addRecordByColumns(cdata);
73          Map<String, Object> idata = new TreeMap<>();
74          idata.put("time", 6);
75          idata.put("value", 9.12);
76          idata.put("remark", "third");
77          table.addRecordByColumnIds(idata);
78          
79          DataRecord record = table.iterator().next();
80          assertArrayEquals(new Object[] {2, 5.0, "normal"}, record.getValues());
81  
82          DataColumn<Double> c1 = new SimpleDataColumn<>("x", "x", double.class);
83          DataColumn<double[][]> c2 = new SimpleDataColumn<>("y", "y", double[][].class);
84          ListDataTable txy = new ListDataTable("xy[][]", "x, y[][]", ImmutableList.of(c1, c2));
85          assertArrayEquals(new String[] {"x", "y"}, txy.getColumnIds());
86          assertArrayEquals(new String[] {"x", "y"}, txy.getColumnDescriptions());
87          assertArrayEquals(new Class<?>[] {double.class, double[][].class}, txy.getColumnDataTypes());
88          assertArrayEquals(new String[] {"double", "[[D"}, txy.getColumnDataTypeStrings());
89          
90          String tableString = table.toString();
91          assertTrue(tableString.startsWith("ListDataTable"));
92          assertTrue(tableString.contains("tableId"));
93          assertTrue(tableString.contains("tableDescription"));
94          assertTrue(tableString.contains("SimpleDataColumn"));
95          
96          String recordString = table.iterator().next().toString();
97          assertTrue(recordString.startsWith("ListDataTable.ListRecord"));
98          assertTrue(recordString.contains("time = 2"));
99          assertTrue(recordString.contains("value = 5.0"));
100     }
101 
102     /** Test the ListTable, the Column and the Record. */
103     @Test
104     public void testSubclassListTable()
105     {
106         // table with double column
107         DataColumn<Double> column = new SimpleDataColumn<>("value", "measured value", double.class);
108         List<DataColumn<?>> columns = new ArrayList<>();
109         columns.add(column);
110         ListDataTable table = new ListDataTable("tableId", "tableDescription", columns);
111 
112         // try double arguments in double column
113         table.addRecord(new Object[] {5.0});
114         Map<DataColumn<?>, Object> cdata = new LinkedHashMap<>();
115         cdata.put(column, 7.7);
116         table.addRecordByColumns(cdata);
117         Map<String, Object> idata = new TreeMap<>();
118         idata.put("value", 9.12);
119         table.addRecordByColumnIds(idata);
120 
121         // try Double arguments in double column
122         ListDataTable table2 = new ListDataTable("tableId", "tableDescription", columns);
123         table2.addRecord(new Object[] {Double.valueOf(5.0)});
124         cdata = new LinkedHashMap<>();
125         cdata.put(column, Double.valueOf(7.7));
126         table2.addRecordByColumns(cdata);
127         idata = new TreeMap<>();
128         idata.put("value", Double.valueOf(9.12));
129         table2.addRecordByColumnIds(idata);
130 
131         // compare both tables
132         tableCompare(table, table2);
133 
134         // table with Number column
135         DataColumn<Number> nColumn = new SimpleDataColumn<>("value", "measured value", Number.class);
136         List<DataColumn<?>> nColumns = new ArrayList<>();
137         nColumns.add(nColumn);
138         ListDataTable nTable = new ListDataTable("tableId", "tableDescription", nColumns);
139 
140         // try double arguments in Number column
141         nTable.addRecord(new Object[] {5.0});
142         Map<DataColumn<?>, Object> cndata = new LinkedHashMap<>();
143         cndata.put(nColumn, 7.7);
144         nTable.addRecordByColumns(cndata);
145         Map<String, Object> indata = new TreeMap<>();
146         indata.put("value", 9.12);
147         nTable.addRecordByColumnIds(indata);
148 
149         // try Double arguments in Number column
150         ListDataTable nTable2 = new ListDataTable("tableId", "tableDescription", nColumns);
151         nTable2.addRecord(new Object[] {Double.valueOf(5.0)});
152         cndata = new LinkedHashMap<>();
153         cndata.put(nColumn, Double.valueOf(7.7));
154         nTable2.addRecordByColumns(cndata);
155         indata = new TreeMap<>();
156         indata.put("value", Double.valueOf(9.12));
157         nTable2.addRecordByColumnIds(indata);
158 
159         // compare both tables
160         tableCompare(nTable, nTable2);
161     }
162 
163     /**
164      * Compare the contents of two tables, where primitive content and wrapped primitive content (e.g., a double and a Double)
165      * are considered the same if the stored value is the same.
166      * @param table1 the first table
167      * @param table2 the second table
168      */
169     public static void tableCompare(final ListDataTable table1, final ListDataTable table2)
170     {
171         assertEquals(table1.getColumns().size(), table2.getColumns().size());
172         assertEquals(table1.getId(), table2.getId());
173         assertEquals(table1.getDescription(), table2.getDescription());
174         assertEquals(table1.getNumberOfColumns(), table2.getNumberOfColumns());
175         assertEquals(table1.getColumns().size(), table2.getColumns().size());
176         for (int i = 0; i < table1.getColumns().size(); i++)
177         {
178             DataColumn<?> c1 = table1.getColumns().get(i);
179             DataColumn<?> c2 = table2.getColumns().get(i);
180             assertEquals(c1.getId(), c2.getId());
181             assertEquals(c1.getDescription(), c2.getDescription());
182             assertTrue(Primitive.isPrimitiveAssignableFrom(c1.getValueType(), c2.getValueType()));
183             assertTrue(Primitive.isPrimitiveAssignableFrom(c2.getValueType(), c1.getValueType()));
184         }
185 
186         Iterator<DataRecord> it2 = table2.iterator();
187         for (DataRecord r1 : table1)
188         {
189             assertTrue(it2.hasNext());
190             DataRecord r2 = it2.next();
191             for (int i = 0; i < table1.getColumns().size(); i++)
192             {
193                 DataColumn<?> c1 = table1.getColumns().get(i);
194                 String c2id = table2.getColumns().get(i).getId();
195                 Object v1 = r1.getValue(c1);
196                 Object v2 = r2.getValue(c2id);
197                 assertEquals(v1.toString(), v2.toString());
198             }
199         }
200         assertFalse(it2.hasNext());
201     }
202 
203     /** Test column and table construction with wrong arguments. */
204     @Test
205     public void testIllegalColumnTable()
206     {
207         DataColumn<Integer> column1 = new SimpleDataColumn<>("time", "time, rounded to second [s]", int.class);
208         DataColumn<Double> column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class);
209         DataColumn<String> column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class);
210         List<DataColumn<?>> columns = new ArrayList<>();
211         columns.add(column1);
212         columns.add(column2);
213         columns.add(column3);
214 
215         //
216         // test illegal columns
217         //
218 
219         try
220         {
221             new SimpleDataColumn<>(null, "measured value [m]", double.class);
222             fail("null id should have thrown NullPointerException");
223         }
224         catch (NullPointerException npe)
225         {
226             // ok
227         }
228         try
229         {
230             new SimpleDataColumn<>("value", null, double.class);
231             fail("null description should have thrown NullPointerException");
232         }
233         catch (NullPointerException npe)
234         {
235             // ok
236         }
237         try
238         {
239             new SimpleDataColumn<>("value", "measured value [m]", null);
240             fail("null valueType should have thrown NullPointerException");
241         }
242         catch (NullPointerException npe)
243         {
244             // ok
245         }
246         try
247         {
248             new SimpleDataColumn<>("", "measured value [m]", double.class);
249             fail("empty id should have thrown IllegalArgumentException");
250         }
251         catch (IllegalArgumentException iae)
252         {
253             // ok
254         }
255 
256         //
257         // test illegal tables
258         //
259 
260         List<DataColumn<?>> cx = new ArrayList<>();
261         cx.add(column1);
262         cx.add(column1); // duplicate
263         cx.add(column3);
264         try
265         {
266             new ListDataTable("tableId", "tableDescription", cx);
267             fail("duplicate column should have thrown IllegalArgumentException");
268         }
269         catch (IllegalArgumentException iae)
270         {
271             // ok
272         }
273 
274         cx = new ArrayList<>();
275         cx.add(column1);
276         cx.add(new SimpleDataColumn<>("time", "another timestamp", double.class));
277         cx.add(column3);
278         try
279         {
280             new ListDataTable("tableId", "tableDescription", cx);
281             fail("duplicate column id should have thrown IllegalArgumentException");
282         }
283         catch (IllegalArgumentException iae)
284         {
285             // ok
286         }
287 
288         try
289         {
290             new ListDataTable(null, "tableDescription", columns);
291             fail("null id should have thrown NullPointerException");
292         }
293         catch (NullPointerException iae)
294         {
295             // ok
296         }
297 
298         try
299         {
300             new ListDataTable("", "tableDescription", columns);
301             fail("empty id should have thrown IllegalArgumentException");
302         }
303         catch (IllegalArgumentException iae)
304         {
305             // ok
306         }
307 
308         try
309         {
310             new ListDataTable("tableId", null, columns);
311             fail("null id should have thrown NullPointerException");
312         }
313         catch (NullPointerException iae)
314         {
315             // ok
316         }
317 
318         try
319         {
320             new ListDataTable("tableId", "tableDescription", (ImmutableList<DataColumn<?>>) null);
321             fail("null columns should have thrown NullPointerException");
322         }
323         catch (NullPointerException iae)
324         {
325             // ok
326         }
327 
328         try
329         {
330             new ListDataTable("tableId", "tableDescription", (Collection<DataColumn<?>>) null);
331             fail("null columns should have thrown NullPointerException");
332         }
333         catch (NullPointerException iae)
334         {
335             // ok
336         }
337 
338         try
339         {
340             new ListDataTable("tableId", "tableDescription", new ArrayList<DataColumn<?>>());
341             fail("zero columns should have thrown IllegalArgumentException");
342         }
343         catch (IllegalArgumentException iae)
344         {
345             // ok
346         }
347     }
348 
349     /** Test column and table construction with wrong value arguments. */
350     @Test
351     public void testIllegalRecordTable()
352     {
353         DataColumn<Integer> column1 = new SimpleDataColumn<>("time", "time, rounded to second [s]", int.class);
354         DataColumn<Double> column2 = new SimpleDataColumn<>("value", "measured value [m]", double.class);
355         DataColumn<String> column3 = new SimpleDataColumn<>("remark", "remark about the measurement", String.class);
356         List<DataColumn<?>> columns = new ArrayList<>();
357         columns.add(column1);
358         columns.add(column2);
359         columns.add(column3);
360         ListDataTable table = new ListDataTable("tableId", "tableDescription", columns);
361 
362         //
363         // test null data
364         //
365 
366         try
367         {
368             table.addRecord((Object[]) null);
369             fail("null data record should have raised exception");
370         }
371         catch (NullPointerException npe)
372         {
373             // ok
374         }
375 
376         try
377         {
378             table.addRecordByColumnIds((Map<String, Object>) null);
379             fail("null data record should have raised exception");
380         }
381         catch (NullPointerException npe)
382         {
383             // ok
384         }
385 
386         try
387         {
388             table.addRecordByColumns((Map<DataColumn<?>, Object>) null);
389             fail("null data record should have raised exception");
390         }
391         catch (NullPointerException npe)
392         {
393             // ok
394         }
395 
396         //
397         // test too few columns data
398         //
399 
400         try
401         {
402             table.addRecord(new Object[] {});
403             fail("empty data record should have raised exception");
404         }
405         catch (IllegalArgumentException iae)
406         {
407             // ok
408         }
409 
410         try
411         {
412             table.addRecordByColumnIds(new HashMap<String, Object>());
413             fail("empty data record should have raised exception");
414         }
415         catch (IllegalArgumentException iae)
416         {
417             // ok
418         }
419 
420         try
421         {
422             table.addRecordByColumns(new HashMap<DataColumn<?>, Object>());
423             fail("empty data record should have raised exception");
424         }
425         catch (IllegalArgumentException iae)
426         {
427             // ok
428         }
429 
430         //
431         // test too many columns data
432         //
433 
434         try
435         {
436             table.addRecord(new Object[] {1, 2, 3, 4});
437             fail("too long data record should have raised exception");
438         }
439         catch (IllegalArgumentException iae)
440         {
441             // ok
442         }
443 
444         try
445         {
446             table.addRecordByColumnIds(ImmutableMap.of("time", 3, "value", 3.5, "remark", "none", "extra", "xx"));
447             fail("too long data record should have raised exception");
448         }
449         catch (IllegalArgumentException iae)
450         {
451             // ok
452         }
453 
454         try
455         {
456             DataColumn<String> column4 = new SimpleDataColumn<>("c4", "column 4", String.class);
457             table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 3.5, column3, "remark", column4, "xx"));
458             fail("too long data record should have raised exception");
459         }
460         catch (IllegalArgumentException iae)
461         {
462             // ok
463         }
464 
465         //
466         // test wrong type data
467         //
468 
469         try
470         {
471             table.addRecord(new Object[] {1, 2, 3});
472             fail("wrong type data record should have raised exception");
473         }
474         catch (IllegalArgumentException iae)
475         {
476             // ok
477         }
478 
479         try
480         {
481             table.addRecordByColumnIds(ImmutableMap.of("time", 3, "value", 2L, "remark", "none"));
482             fail("wrong type data record should have raised exception");
483         }
484         catch (IllegalArgumentException iae)
485         {
486             // ok
487         }
488 
489         try
490         {
491             table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 3.5, column3, 2L));
492             fail("wrong type data record should have raised exception");
493         }
494         catch (IllegalArgumentException iae)
495         {
496             // ok
497         }
498 
499         //
500         // test missing column data, where the number of columns is okay
501         //
502 
503         try
504         {
505             table.addRecordByColumnIds(ImmutableMap.of("time", 3, "remark", "none", "wrong", 3));
506             fail("wrong type data record should have raised exception");
507         }
508         catch (IllegalArgumentException iae)
509         {
510             // ok
511         }
512 
513         try
514         {
515             DataColumn<String> column4 = new SimpleDataColumn<>("c4", "column 4", String.class);
516             table.addRecordByColumns(ImmutableMap.of(column1, 3, column2, 4.5, column4, "xx"));
517             fail("wrong type data record should have raised exception");
518         }
519         catch (IllegalArgumentException iae)
520         {
521             // ok
522         }
523 
524     }
525 
526 }