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