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