1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.schemaspy.model;
20
21 import java.sql.DatabaseMetaData;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import java.util.regex.Pattern;
33 import net.sourceforge.schemaspy.model.xml.TableColumnMeta;
34
35 public class TableColumn {
36 private final Table table;
37 private final String name;
38 private final Object id;
39 private final String type;
40 private final int length;
41 private final int decimalDigits;
42 private final String detailedSize;
43 private final boolean isNullable;
44 private boolean isAutoUpdated;
45 private Boolean isUnique;
46 private final Object defaultValue;
47 private String comments;
48 private final Map<TableColumn, ForeignKeyConstraint> parents = new HashMap<TableColumn, ForeignKeyConstraint>();
49 private final Map<TableColumn, ForeignKeyConstraint> children = new TreeMap<TableColumn, ForeignKeyConstraint>(new ColumnComparator());
50 private boolean allowImpliedParents = true;
51 private boolean allowImpliedChildren = true;
52 private boolean isExcluded = false;
53 private boolean isAllExcluded = false;
54 private static final Logger logger = Logger.getLogger(TableColumn.class.getName());
55 private static final boolean finerEnabled = logger.isLoggable(Level.FINER);
56
57
58
59
60
61
62
63
64 TableColumn(Table table, ResultSet rs, Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
65 this.table = table;
66
67
68
69
70 String tmp = rs.getString("COLUMN_NAME");
71 name = tmp == null ? null : tmp.intern();
72 tmp = rs.getString("TYPE_NAME");
73 type = tmp == null ? "unknown" : tmp.intern();
74
75 decimalDigits = rs.getInt("DECIMAL_DIGITS");
76 Number bufLength = (Number)rs.getObject("BUFFER_LENGTH");
77 if (bufLength != null && bufLength.shortValue() > 0)
78 length = bufLength.shortValue();
79 else
80 length = rs.getInt("COLUMN_SIZE");
81
82 StringBuilder buf = new StringBuilder();
83 buf.append(length);
84 if (decimalDigits > 0) {
85 buf.append(',');
86 buf.append(decimalDigits);
87 }
88 detailedSize = buf.toString();
89
90 isNullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
91 defaultValue = rs.getString("COLUMN_DEF");
92 setComments(rs.getString("REMARKS"));
93 id = new Integer(rs.getInt("ORDINAL_POSITION") - 1);
94
95 isAllExcluded = matches(excludeColumns);
96 isExcluded = isAllExcluded || matches(excludeIndirectColumns);
97 if (isExcluded && finerEnabled) {
98 logger.finer("Excluding column " + getTable() + '.' + getName() +
99 ": matches " + excludeColumns + ":" + isAllExcluded + " " +
100 excludeIndirectColumns + ":" + matches(excludeIndirectColumns));
101 }
102 }
103
104
105
106
107
108
109
110
111 public TableColumn(Table table, TableColumnMeta colMeta) {
112 this.table = table;
113 name = colMeta.getName();
114 id = null;
115 type = "Unknown";
116 length = 0;
117 decimalDigits = 0;
118 detailedSize = "";
119 isNullable = false;
120 isAutoUpdated = false;
121 defaultValue = null;
122 comments = colMeta.getComments();
123 }
124
125
126
127
128
129
130 public Table getTable() {
131 return table;
132 }
133
134
135
136
137
138
139 public String getName() {
140 return name;
141 }
142
143
144
145
146
147
148 public Object getId() {
149 return id;
150 }
151
152
153
154
155
156
157 public String getType() {
158 return type;
159 }
160
161
162
163
164
165
166
167 public int getLength() {
168 return length;
169 }
170
171
172
173
174
175
176
177 public int getDecimalDigits() {
178 return decimalDigits;
179 }
180
181
182
183
184
185
186 public String getDetailedSize() {
187 return detailedSize;
188 }
189
190
191
192
193
194
195 public boolean isNullable() {
196 return isNullable;
197 }
198
199
200
201
202
203
204 public boolean isAutoUpdated() {
205 return isAutoUpdated;
206 }
207
208
209
210
211
212
213 public void setIsAutoUpdated(boolean isAutoUpdated) {
214 this.isAutoUpdated = isAutoUpdated;
215 }
216
217
218
219
220
221
222 public boolean isUnique() {
223 if (isUnique == null) {
224
225 for (TableIndex index : table.getIndexes()) {
226 if (index.isUnique()) {
227 List<TableColumn> indexColumns = index.getColumns();
228 if (indexColumns.size() == 1 && indexColumns.contains(this)) {
229 isUnique = true;
230 break;
231 }
232 }
233 }
234
235 if (isUnique == null) {
236
237 isUnique = table.getPrimaryColumns().size() == 1 && isPrimary();
238 }
239 }
240
241 return isUnique;
242 }
243
244
245
246
247
248
249 public boolean isPrimary() {
250 return table.getPrimaryColumns().contains(this);
251 }
252
253
254
255
256
257
258 public boolean isForeignKey() {
259 return !parents.isEmpty();
260 }
261
262
263
264
265
266
267 public Object getDefaultValue() {
268 return defaultValue;
269 }
270
271
272
273
274 public String getComments() {
275 return comments;
276 }
277
278
279
280
281
282 public void setComments(String comments) {
283 this.comments = (comments == null || comments.trim().length() == 0) ? null : comments.trim();
284 }
285
286
287
288
289
290
291
292
293
294
295
296 public boolean isExcluded() {
297 return isExcluded;
298 }
299
300
301
302
303
304
305
306
307
308
309 public boolean isAllExcluded() {
310 return isAllExcluded;
311 }
312
313
314
315
316
317
318
319 public void addParent(TableColumn parent, ForeignKeyConstraint constraint) {
320 parents.put(parent, constraint);
321 table.addedParent();
322 }
323
324
325
326
327
328
329 public void removeParent(TableColumn parent) {
330 parents.remove(parent);
331 }
332
333
334
335
336 public void unlinkParents() {
337 for (TableColumn parent : parents.keySet()) {
338 parent.removeChild(this);
339 }
340 parents.clear();
341 }
342
343
344
345
346
347
348 public Set<TableColumn> getParents() {
349 return parents.keySet();
350 }
351
352
353
354
355 public ForeignKeyConstraint getParentConstraint(TableColumn parent) {
356 return parents.get(parent);
357 }
358
359
360
361
362
363
364 public ForeignKeyConstraint removeAParentFKConstraint() {
365 for (TableColumn relatedColumn : parents.keySet()) {
366 ForeignKeyConstraint constraint = parents.remove(relatedColumn);
367 relatedColumn.removeChild(this);
368 return constraint;
369 }
370
371 return null;
372 }
373
374
375
376
377
378
379 public ForeignKeyConstraint removeAChildFKConstraint() {
380 for (TableColumn relatedColumn : children.keySet()) {
381 ForeignKeyConstraint constraint = children.remove(relatedColumn);
382 relatedColumn.removeParent(this);
383 return constraint;
384 }
385
386 return null;
387 }
388
389
390
391
392
393
394
395 public void addChild(TableColumn child, ForeignKeyConstraint constraint) {
396 children.put(child, constraint);
397 table.addedChild();
398 }
399
400
401
402
403
404
405 public void removeChild(TableColumn child) {
406 children.remove(child);
407 }
408
409
410
411
412 public void unlinkChildren() {
413 for (TableColumn child : children.keySet())
414 child.removeParent(this);
415 children.clear();
416 }
417
418
419
420
421
422
423 public Set<TableColumn> getChildren() {
424 return children.keySet();
425 }
426
427
428
429
430
431 public ForeignKeyConstraint getChildConstraint(TableColumn child) {
432 return children.get(child);
433 }
434
435
436
437
438
439
440
441
442 public boolean matches(Pattern regex) {
443 return regex.matcher(getTable().getName() + '.' + getName()).matches();
444 }
445
446
447
448
449
450
451
452 public void update(TableColumnMeta colMeta) {
453 String newComments = colMeta.getComments();
454 if (newComments != null)
455 setComments(newComments);
456
457 if (!isPrimary() && colMeta.isPrimary()) {
458 table.setPrimaryColumn(this);
459 }
460
461 allowImpliedParents = !colMeta.isImpliedParentsDisabled();
462 allowImpliedChildren = !colMeta.isImpliedChildrenDisabled();
463 isExcluded |= colMeta.isExcluded();
464 isAllExcluded |= colMeta.isAllExcluded();
465 }
466
467
468
469
470 @Override
471 public String toString() {
472 return getName();
473 }
474
475
476
477
478 private class ColumnComparator implements Comparator<TableColumn> {
479 public int compare(TableColumn column1, TableColumn column2) {
480 int rc = column1.getTable().compareTo(column2.getTable());
481 if (rc == 0)
482 rc = column1.getName().compareToIgnoreCase(column2.getName());
483 return rc;
484 }
485 }
486
487
488
489
490
491
492
493 public boolean allowsImpliedParents() {
494 return allowImpliedParents;
495 }
496
497
498
499
500
501
502
503 public boolean allowsImpliedChildren() {
504 return allowImpliedChildren;
505 }
506 }