1 package org.djutils.data;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Spliterator;
11 import java.util.stream.Stream;
12
13 import org.djunits.Throw;
14 import org.djutils.immutablecollections.Immutable;
15 import org.djutils.immutablecollections.ImmutableArrayList;
16 import org.djutils.immutablecollections.ImmutableLinkedHashMap;
17 import org.djutils.immutablecollections.ImmutableList;
18 import org.djutils.immutablecollections.ImmutableMap;
19 import org.djutils.primitives.Primitive;
20
21
22
23
24
25
26
27
28
29
30
31 public class ListDataTable extends AbstractDataTable
32 {
33
34
35 private List<DataRecord> records = Collections.synchronizedList(new ArrayList<>());
36
37
38 private Map<DataColumn<?>, Integer> columnNumbers = new LinkedHashMap<>();
39
40
41 private Map<String, Integer> idNumbers = new LinkedHashMap<>();
42
43
44
45
46
47
48
49
50 public ListDataTable(final String id, final String description, final Collection<DataColumn<?>> columns)
51 {
52 this(id, description, new ImmutableArrayList<DataColumn<?>>(columns));
53 }
54
55
56
57
58
59
60
61
62 public ListDataTable(final String id, final String description, final ImmutableList<DataColumn<?>> columns)
63 {
64 super(id, description, columns);
65 for (int index = 0; index < getColumns().size(); index++)
66 {
67 DataColumn<?> column = getColumns().get(index);
68 this.columnNumbers.put(column, index);
69 this.idNumbers.put(column.getId(), index);
70 }
71 Throw.when(getNumberOfColumns() != this.idNumbers.size(), IllegalArgumentException.class,
72 "Duplicate column ids are not allowed.");
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 @Override
96 public Iterator<DataRecord> iterator()
97 {
98 return this.records.iterator();
99 }
100
101
102 @Override
103 public boolean isEmpty()
104 {
105 return this.records.isEmpty();
106 }
107
108
109
110
111
112
113
114
115 public void addRecordByColumns(final Map<DataColumn<?>, Object> data)
116 {
117 Throw.whenNull(data, "Data may not be null.");
118 addRecordByColumns(new ImmutableLinkedHashMap<>(data, Immutable.WRAP));
119 }
120
121
122
123
124
125
126
127
128 public void addRecordByColumns(final ImmutableMap<DataColumn<?>, Object> data)
129 {
130 Throw.whenNull(data, "Data may not be null.");
131 Throw.when(data.size() != getNumberOfColumns(), IllegalArgumentException.class,
132 "Number of data columns doesn't match number of table columns.");
133 Object[] dataObjects = new Object[getNumberOfColumns()];
134 for (int index = 0; index < getColumns().size(); index++)
135 {
136 DataColumn<?> column = getColumns().get(index);
137 Throw.when(!data.containsKey(column), IllegalArgumentException.class, "Missing data for column %s", column.getId());
138 Object value = data.get(column);
139 Throw.when(!Primitive.isPrimitiveAssignableFrom(column.getValueType(), value.getClass()),
140 IllegalArgumentException.class, "Data value for column %s is not of type %s, but of type %s.",
141 column.getId(), column.getValueType(), value.getClass());
142 dataObjects[index] = value;
143 }
144 this.records.add(new ListRecord(dataObjects));
145 }
146
147
148
149
150
151
152
153 public void addRecordByColumnIds(final Map<String, Object> data)
154 {
155 Throw.whenNull(data, "Data may not be null.");
156 addRecordByColumnIds(new ImmutableLinkedHashMap<>(data, Immutable.WRAP));
157 }
158
159
160
161
162
163
164
165
166 public void addRecordByColumnIds(final ImmutableMap<String, Object> data)
167 {
168 Throw.whenNull(data, "Data may not be null.");
169 Throw.when(data.size() != getNumberOfColumns(), IllegalArgumentException.class,
170 "Number of data columns doesn't match number of table columns.");
171 Object[] dataObjects = new Object[getNumberOfColumns()];
172 for (int index = 0; index < getColumns().size(); index++)
173 {
174 DataColumn<?> column = getColumns().get(index);
175 Throw.when(!data.containsKey(column.getId()), IllegalArgumentException.class, "Missing data for column %s",
176 column.getId());
177 Object value = data.get(column.getId());
178 Class<?> dataClass = value.getClass();
179 Throw.when(!Primitive.isPrimitiveAssignableFrom(column.getValueType(), dataClass), IllegalArgumentException.class,
180 "Data value for column %s is not of type %s, but of type %s.", column.getId(), column.getValueType(),
181 dataClass);
182 dataObjects[index] = value;
183 }
184 this.records.add(new ListRecord(dataObjects));
185 }
186
187
188
189
190
191
192
193
194 public void addRecord(final Object[] data)
195 {
196 Throw.whenNull(data, "Data may not be null.");
197 Throw.when(data.length != getNumberOfColumns(), IllegalArgumentException.class,
198 "Number of data columns doesn't match number of table columns.");
199 Object[] dataObjects = new Object[getNumberOfColumns()];
200 for (int index = 0; index < getColumns().size(); index++)
201 {
202 DataColumn<?> column = getColumns().get(index);
203 Class<?> dataClass = data[index].getClass();
204 Throw.when(!Primitive.isPrimitiveAssignableFrom(column.getValueType(), dataClass), IllegalArgumentException.class,
205 "Data value for column %s is not of type %s, but of type %s.", column.getId(), column.getValueType(),
206 dataClass);
207 dataObjects[index] = data[index];
208 }
209 this.records.add(new ListRecord(dataObjects));
210 }
211
212
213 @Override
214 public String toString()
215 {
216 StringBuilder result = new StringBuilder();
217 result.append("ListDataTable [getId()=");
218 result.append(this.getId());
219 result.append(", getDescription()=");
220 result.append(this.getDescription());
221 result.append("]\nColumns:\n");
222 for (DataColumn<?> column : getColumns())
223 {
224 result.append(" ");
225 result.append(column.toString());
226 result.append("\n");
227 }
228 return result.toString();
229 }
230
231
232 public class ListRecord implements DataRecord
233 {
234
235
236 private final Object[] values;
237
238
239
240
241
242 public ListRecord(final Object[] values)
243 {
244 this.values = values;
245 }
246
247
248 @SuppressWarnings({"unchecked", "synthetic-access"})
249 @Override
250 public <T> T getValue(final DataColumn<T> column)
251 {
252 return (T) this.values[ListDataTable.this.columnNumbers.get(column)];
253 }
254
255
256 @SuppressWarnings("synthetic-access")
257 @Override
258 public Object getValue(final String id)
259 {
260 return this.values[ListDataTable.this.idNumbers.get(id)];
261 }
262
263
264 @Override
265 public Object[] getValues()
266 {
267 return this.values;
268 }
269
270
271 @Override
272 public String toString()
273 {
274 StringBuilder result = new StringBuilder();
275 result.append("ListDataTable.ListRecord\n");
276 for (DataColumn<?> column : ListDataTable.this.getColumns())
277 {
278 result.append(" ");
279 result.append(column.getId());
280 result.append(" = ");
281 result.append(getValue(column.getId()));
282 result.append("\n");
283 }
284 return result.toString();
285 }
286
287 }
288
289 }