1 package guru.mikelue.jdut.datagrain;
2
3 import java.sql.DatabaseMetaData;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Optional;
10 import java.util.function.Consumer;
11 import java.util.stream.Collectors;
12 import java.util.stream.IntStream;
13 import java.util.stream.Stream;
14
15 import org.apache.commons.lang3.StringUtils;
16 import org.apache.commons.lang3.Validate;
17 import org.apache.commons.lang3.builder.EqualsBuilder;
18 import org.apache.commons.lang3.builder.HashCodeBuilder;
19
20 import guru.mikelue.jdut.jdbc.util.MetaDataWorker;
21
22
23
24
25
26
27
28
29
30
31
32
33 public class SchemaTable {
34 private final static String NULL_NAMING = "<null>";
35
36 private Optional<String> catalog = Optional.empty();
37 private Optional<String> schema = Optional.empty();
38
39 private String name;
40 private String quotedTableName;
41
42 private MetaDataWorker metaDataWorker;
43
44 private List<String> keys = Collections.emptyList();
45 private Map<String, SchemaColumn> columns;
46 private Map<String, Integer> nameToIndex;
47 private Map<Integer, String> indexToName;
48
49
50
51
52 public class Builder {
53 private Builder() {}
54
55
56
57
58
59
60
61
62 public Builder metaDataWorker(MetaDataWorker newWorker)
63 {
64 metaDataWorker = newWorker;
65 return this;
66 }
67
68
69
70
71
72
73
74
75
76
77 public Builder catalog(String newCatalog)
78 {
79 catalog = Optional.ofNullable(StringUtils.trimToNull(newCatalog))
80 .map(SchemaTable.this::treatIdentifier);
81 return this;
82 }
83
84
85
86
87
88
89
90
91
92
93 public Builder schema(String newSchema)
94 {
95 schema = Optional.ofNullable(StringUtils.trimToNull(newSchema))
96 .map(SchemaTable.this::treatIdentifier);
97
98 return this;
99 }
100
101
102
103
104
105
106
107
108 public Builder name(String newName)
109 {
110 name = StringUtils.trimToNull(newName);
111 Validate.notNull(name, "Need table name");
112
113 name = SchemaTable.this.treatIdentifier(name);
114
115 return this;
116 }
117
118
119
120
121
122
123
124
125 public Builder keys(String... newKeys)
126 {
127 keys = Stream.of(newKeys)
128 .map(key -> {
129 String processedKey = StringUtils.trimToNull(key);
130 if (processedKey == null) {
131 return null;
132 }
133
134 return SchemaTable.this.treatIdentifier(processedKey);
135 })
136 .filter(key -> key != null)
137 .collect(Collectors.toList());
138
139 return this;
140 }
141
142
143
144
145
146
147
148
149 public Builder column(SchemaColumn column)
150 {
151 String columnName = SchemaTable.this.treatIdentifier(column.getName());
152
153 if (!nameToIndex.containsKey(columnName)) {
154 nameToIndex.put(columnName, nameToIndex.size());
155 indexToName.put(nameToIndex.get(columnName), columnName);
156 }
157
158 columns.put(columnName, column);
159
160 return this;
161 }
162
163 private void initQuotedName()
164 {
165 if (metaDataWorker == null) {
166 quotedTableName = name;
167 return;
168 }
169
170 if (metaDataWorker.supportsSchemasInDataManipulation()) {
171 quotedTableName = schema
172 .map(s -> metaDataWorker.quoteIdentifier(s) + ".")
173 .orElse("") +
174 metaDataWorker.quoteIdentifier(name);
175 } else {
176 quotedTableName = metaDataWorker.quoteIdentifier(name);
177 }
178 }
179 }
180
181
182
183
184
185
186
187
188 public static SchemaTable build(Consumer<Builder> builderConsumer)
189 {
190 SchemaTablee.html#SchemaTable">SchemaTable tableSchema = new SchemaTable();
191 SchemaTable.Builder tableBuilder = tableSchema.new Builder();
192 tableSchema.columns = new HashMap<>(CollectionUsage.HASH_SPACE_OF_COLUMNS);
193 tableSchema.indexToName = new HashMap<>(CollectionUsage.HASH_SPACE_OF_COLUMNS);
194 tableSchema.nameToIndex = new HashMap<>(CollectionUsage.HASH_SPACE_OF_COLUMNS);
195
196 builderConsumer.accept(tableBuilder);
197 tableBuilder.initQuotedName();
198
199 return tableSchema.clone();
200 }
201
202 private SchemaTable() {}
203
204
205
206
207 public MetaDataWorker getMetaDataWorker()
208 {
209 return metaDataWorker;
210 }
211
212
213
214
215
216
217 public String getName()
218 {
219 return name;
220 }
221
222
223
224
225
226
227
228
229
230 public String getQuotedFullName()
231 {
232 return quotedTableName;
233 }
234
235
236
237
238
239
240 public Optional<String> getSchema()
241 {
242 return schema;
243 }
244
245
246
247
248
249
250 public Optional<String> getCatalog()
251 {
252 return catalog;
253 }
254
255
256
257
258
259
260 public List<String> getKeys()
261 {
262 return keys;
263 }
264
265
266
267
268
269
270 public int getNumberOfColumns()
271 {
272 return columns.size();
273 }
274
275
276
277
278
279
280 public List<SchemaColumn> getColumns()
281 {
282 List<SchemaColumn> result = new ArrayList<>(getNumberOfColumns());
283 IntStream.range(0, getNumberOfColumns())
284 .forEach(
285 i -> result.add(getColumn(i))
286 );
287
288 return result;
289 }
290
291
292
293
294
295
296
297
298 public boolean hasColumn(String columnName)
299 {
300 return columns.containsKey(treatIdentifier(columnName));
301 }
302
303
304
305
306
307
308
309
310
311
312 public SchemaColumn getColumn(String columnName)
313 {
314 SchemaColumn column = columns.get(treatIdentifier(columnName));
315 if (column == null) {
316 throw new IllegalArgumentException(String.format("Cannot find column: \"%s\"", columnName));
317 }
318
319 return column;
320 }
321
322
323
324
325
326
327
328
329
330
331 public SchemaColumn getColumn(int columnIndex)
332 {
333 String columnName = indexToName.get(columnIndex);
334 if (columnName == null) {
335 throw new IllegalArgumentException(String.format("Cannot find column by index: \"%d\"", columnIndex));
336 }
337
338 return getColumn(columnName);
339 }
340
341
342
343
344
345
346
347
348 public String treatIdentifier(String identifier)
349 {
350 identifier = StringUtils.trimToEmpty(identifier);
351 if (StringUtils.isEmpty(identifier)) {
352 return identifier;
353 }
354
355 if (metaDataWorker == null) {
356 return identifier.toLowerCase();
357 }
358
359 return metaDataWorker.processIdentifier(identifier);
360 }
361
362
363
364
365
366
367
368
369 public String quoteIdentifier(String identifier)
370 {
371 if (metaDataWorker == null) {
372 return identifier;
373 }
374
375 return metaDataWorker.quoteIdentifier(identifier);
376 }
377
378
379
380
381
382
383
384
385 public String getFullTableName()
386 {
387 return String.format(
388 "%s.%s.%s|%s",
389 catalog.orElse(NULL_NAMING), schema.orElse(NULL_NAMING),
390 name,
391 keys.stream().collect(
392 Collectors.joining(",")
393 )
394 );
395 }
396
397
398
399
400 @Override
401 protected SchemaTable clone()
402 {
403 SchemaTable clonedObject = safeClone();
404
405 clonedObject.keys = Collections.unmodifiableList(this.keys);
406 clonedObject.columns = Collections.unmodifiableMap(this.columns);
407 clonedObject.nameToIndex = Collections.unmodifiableMap(this.nameToIndex);
408 clonedObject.indexToName = Collections.unmodifiableMap(this.indexToName);
409
410 return clonedObject;
411 }
412
413 private SchemaTable safeClone()
414 {
415 SchemaTable.html#SchemaTable">SchemaTable clonedObject = new SchemaTable();
416 clonedObject.quotedTableName = this.quotedTableName;
417 clonedObject.name = this.name;
418 clonedObject.schema = this.schema;
419 clonedObject.catalog = this.catalog;
420 clonedObject.metaDataWorker = this.metaDataWorker;
421 return clonedObject;
422 }
423
424
425
426
427 @Override
428 public boolean equals(Object obj)
429 {
430 if (obj == null) { return false; }
431 if (obj == this) { return true; }
432 if (obj.getClass() != getClass()) {
433 return false;
434 }
435
436 SchemaTable rhs = (SchemaTable)obj;
437 return new EqualsBuilder()
438 .append(this.name, rhs.name)
439 .append(this.columns, rhs.columns)
440 .append(this.keys, rhs.keys)
441 .isEquals();
442 }
443
444
445
446
447 @Override
448 public int hashCode()
449 {
450 return new HashCodeBuilder(494534511, 401552629)
451 .append(name)
452 .append(keys)
453 .append(columns)
454 .toHashCode();
455 }
456
457 @Override
458 public String toString()
459 {
460 return String.format(
461 "Table: %s.%s.\"%s\". Columns [%s]",
462 catalog.orElse(NULL_NAMING),
463 schema.orElse(NULL_NAMING),
464 name,
465 columns.values().stream()
466 .map(column -> column.getName())
467 .collect(Collectors.joining(",", "\"", "\""))
468 );
469 }
470 }
471