1 package guru.mikelue.jdut.datagrain;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Optional;
9 import java.util.function.Consumer;
10 import java.util.function.Supplier;
11 import java.util.stream.Stream;
12
13 import org.apache.commons.lang3.StringUtils;
14 import org.apache.commons.lang3.Validate;
15 import org.apache.commons.lang3.builder.EqualsBuilder;
16 import org.apache.commons.lang3.builder.HashCodeBuilder;
17
18
19
20
21
22
23 public class DataRow {
24 private SchemaTable tableSchema;
25 private Map<String, DataField<?>> data;
26 private Map<String, Object> attributes = new HashMap<>(8);
27 private boolean validated = false;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class Builder {
52 private DataField.Factory fieldFactory = null;
53
54 private Builder() {}
55
56
57
58
59
60
61
62
63 public Builder tableSchema(SchemaTable newTableSchema)
64 {
65 Validate.notNull(newTableSchema, "Need viable table schema");
66
67 fieldFactory = new DataField.Factory(newTableSchema);
68
69
70
71
72 Map<String, DataField<?>> dataWithNewTableSchema = new HashMap<>(data.size());
73
74 data.entrySet().forEach(
75 fieldEntry -> dataWithNewTableSchema.put(
76 newTableSchema.treatIdentifier(
77 fieldEntry.getKey()
78 ),
79 fieldFactory.clone(fieldEntry.getValue())
80 )
81 );
82
83 tableSchema = newTableSchema;
84 return data(dataWithNewTableSchema);
85
86 }
87
88
89
90
91
92
93
94 public Builder data(Map<String, DataField<?>> newData)
95 {
96 Validate.notNull(newData, "Need viable data");
97
98 data = newData;
99 return this;
100 }
101
102
103
104
105
106
107
108
109
110
111 public <T> Builder fieldOfValue(String columnName, T value)
112 {
113 return field(
114 fieldFactory.composeData(columnName, value)
115 );
116 }
117
118
119
120
121
122
123
124
125
126 public <T> Builder fieldOfValueSupplier(String columnName, Supplier<T> valueSupplier)
127 {
128 return field(
129 fieldFactory.composeDataSupplier(columnName, valueSupplier)
130 );
131 }
132
133
134
135
136
137
138 public boolean getValidated()
139 {
140 return validated;
141 }
142
143
144
145
146
147
148 public void validate() throws DataRowException
149 {
150 DataRow.this.validate();
151 }
152
153
154
155
156
157
158
159
160 public String getTableName()
161 {
162 return tableSchema.getName();
163 }
164
165
166
167
168
169
170
171
172 public SchemaTable getTable()
173 {
174 return tableSchema;
175 }
176
177
178
179
180
181
182
183
184
185 public <T> Optional<T> getData(String columnName)
186 {
187 return this.<T>getDataField(columnName).map(
188 dataField -> dataField.getData()
189 );
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 @SuppressWarnings("unchecked")
218 public <T> Optional<Supplier<T>> getDataSupplier(String columnName)
219 {
220 return this.<T>getDataField(columnName).map(
221 dataField -> (Supplier<T>)dataField.getDataSupplier().orElse(null)
222 );
223 }
224
225
226
227
228
229
230
231
232
233 @SuppressWarnings("unchecked")
234 public <T> Optional<DataField<T>> getDataField(String columnName)
235 {
236 columnName = StringUtils.trimToNull(columnName);
237 Validate.notNull(columnName, "Need viable name of column");
238
239 return Optional.ofNullable((DataField<T>)data.get(columnName));
240 }
241
242
243
244
245
246
247 public Stream<DataField<?>> getStreamOfFields()
248 {
249 return data.values().stream();
250 }
251
252 private <T> Builder field(DataField<T> field)
253 {
254 data.put(
255 tableSchema.treatIdentifier(
256 field.getColumnName()
257 ),
258 field
259 );
260 return this;
261 }
262 }
263
264 private DataRow() {}
265
266
267
268
269
270
271
272
273 public static DataRow build(Consumer<Builder> builderConsumer)
274 {
275 DataRow newRow = new DataRow();
276 newRow.data = new HashMap<>(CollectionUsage.HASH_SPACE_OF_COLUMNS);
277 Builder newBuilder = newRow.new Builder();
278
279 builderConsumer.accept(newBuilder);
280
281 return newRow.clone();
282 }
283
284
285
286
287
288
289
290
291
292 public static DataRown/DataRow.html#DataRow">DataRow build(Consumer<Builder> builderConsumer, DataRow existingRow)
293 {
294 DataRow newRow = existingRow.modifiableClone();
295 Builder newBuilder = newRow.new Builder();
296 newBuilder.tableSchema(existingRow.getTable());
297
298 builderConsumer.accept(newBuilder);
299
300 return newRow.clone();
301 }
302
303
304
305
306
307
308 public SchemaTable getTable()
309 {
310 return tableSchema;
311 }
312
313
314
315
316
317
318 public List<String> getColumns()
319 {
320 return new ArrayList<>(data.keySet());
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335 @SuppressWarnings("unchecked")
336 public <T> DataField<T> getDataField(String columnName)
337 {
338 DataField<T> dataField = (DataField<T>)data.get(
339 tableSchema.treatIdentifier(columnName)
340 );
341 if (dataField == null) {
342 throw new IllegalArgumentException(String.format("Cannot find column: \"%s\"", columnName));
343 }
344
345 return dataField;
346 }
347
348
349
350
351
352
353
354
355
356
357
358
359
360 public <T> T getData(String columnName)
361 {
362 return this.<T>getDataField(columnName).getData();
363 }
364
365
366
367
368
369
370
371
372 public void validate() throws DataRowException
373 {
374 if (validated) {
375 return;
376 }
377
378
379
380
381 for (DataField<?> dataField: data.values()) {
382 if (!tableSchema.hasColumn(dataField.getColumnName())) {
383 throw new MissedColumnException(tableSchema, dataField.getColumn());
384 }
385 }
386
387
388 validated = true;
389 }
390
391
392
393
394
395
396
397
398 public boolean isValidated()
399 {
400 return validated;
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414 @SuppressWarnings("unchecked")
415 public <T> T getAttribute(String name)
416 {
417 return (T)attributes.get(name);
418 }
419
420
421
422
423
424
425
426
427
428
429 public boolean hasAttribute(String name)
430 {
431 return attributes.containsKey(name);
432 }
433
434
435
436
437
438
439
440
441
442 public void putAttribute(String name, Object value)
443 {
444 attributes.put(name, value);
445 }
446
447 @Override
448 protected DataRow clone()
449 {
450 DataRow newRow = new DataRow();
451 newRow.tableSchema = this.tableSchema;
452 newRow.data = Collections.unmodifiableMap(this.data);
453 newRow.validated = this.validated;
454 newRow.attributes = new HashMap<>(this.attributes);
455
456 return newRow;
457 }
458
459 private DataRow modifiableClone()
460 {
461 DataRow newRow = new DataRow();
462 newRow.tableSchema = this.tableSchema;
463 newRow.data = new HashMap<>(this.data);
464 newRow.validated = this.validated;
465 newRow.attributes = new HashMap<>(this.attributes);
466
467 return newRow;
468 }
469
470
471
472
473 @Override
474 public int hashCode()
475 {
476 return new HashCodeBuilder(82782651, 925373735)
477 .append(tableSchema)
478 .append(data)
479 .toHashCode();
480 }
481
482
483
484
485 @Override
486 public boolean equals(Object obj)
487 {
488 if (obj == null) { return false; }
489 if (obj == this) { return true; }
490 if (obj.getClass() != getClass()) {
491 return false;
492 }
493
494 DataRow rhs = (DataRow)obj;
495 return new EqualsBuilder()
496 .append(this.tableSchema, rhs.tableSchema)
497 .append(this.data, rhs.data)
498 .isEquals();
499 }
500 }