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.PreparedStatement;
23 import java.sql.ResultSet;
24 import java.sql.ResultSetMetaData;
25 import java.sql.SQLException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Properties;
34 import java.util.Set;
35 import java.util.TreeMap;
36 import java.util.TreeSet;
37 import java.util.logging.Logger;
38 import java.util.regex.Pattern;
39 import net.sourceforge.schemaspy.Config;
40 import net.sourceforge.schemaspy.model.xml.ForeignKeyMeta;
41 import net.sourceforge.schemaspy.model.xml.TableColumnMeta;
42 import net.sourceforge.schemaspy.model.xml.TableMeta;
43 import net.sourceforge.schemaspy.util.CaseInsensitiveMap;
44
45
46
47
48
49
50
51 public class Table implements Comparable<Table> {
52 private final String schema;
53 private final String name;
54 protected final CaseInsensitiveMap<TableColumn> columns = new CaseInsensitiveMap<TableColumn>();
55 private final List<TableColumn> primaryKeys = new ArrayList<TableColumn>();
56 private final CaseInsensitiveMap<ForeignKeyConstraint> foreignKeys = new CaseInsensitiveMap<ForeignKeyConstraint>();
57 private final CaseInsensitiveMap<TableIndex> indexes = new CaseInsensitiveMap<TableIndex>();
58 private Object id;
59 private final Map<String, String> checkConstraints = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
60 private Long numRows;
61 protected final Database db;
62 protected final Properties properties;
63 private String comments;
64 private int maxChildren;
65 private int maxParents;
66 private final static Logger logger = Logger.getLogger(Table.class.getName());
67
68
69
70
71
72
73
74
75
76
77
78
79
80 public Table(Database db, String schema, String name, String comments, Properties properties, Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
81 this.schema = schema;
82 this.name = name;
83 this.db = db;
84 this.properties = properties;
85 logger.fine("Creating " + getClass().getSimpleName().toLowerCase() + " " +
86 schema == null ? name : (schema + '.' + name));
87 setComments(comments);
88 initColumns(excludeIndirectColumns, excludeColumns);
89 initIndexes();
90 initPrimaryKeys(db.getMetaData());
91 }
92
93
94
95
96
97
98
99
100
101
102 public void connectForeignKeys(Map<String, Table> tables, Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
103 ResultSet rs = null;
104
105 try {
106 rs = db.getMetaData().getImportedKeys(null, getSchema(), getName());
107
108 while (rs.next()) {
109 addForeignKey(rs.getString("FK_NAME"), rs.getString("FKCOLUMN_NAME"),
110 rs.getString("PKTABLE_SCHEM"), rs.getString("PKTABLE_NAME"),
111 rs.getString("PKCOLUMN_NAME"),
112 rs.getInt("UPDATE_RULE"), rs.getInt("DELETE_RULE"),
113 tables, excludeIndirectColumns, excludeColumns);
114 }
115 } finally {
116 if (rs != null)
117 rs.close();
118 }
119
120
121
122
123 if (getSchema() != null) {
124 try {
125 rs = db.getMetaData().getExportedKeys(null, getSchema(), getName());
126
127 while (rs.next()) {
128 String otherSchema = rs.getString("FKTABLE_SCHEM");
129 if (!getSchema().equals(otherSchema))
130 db.addRemoteTable(otherSchema, rs.getString("FKTABLE_NAME"), getSchema(), properties, excludeIndirectColumns, excludeColumns);
131 }
132 } finally {
133 if (rs != null)
134 rs.close();
135 }
136 }
137 }
138
139
140
141
142
143
144 public Collection<ForeignKeyConstraint> getForeignKeys() {
145 return Collections.unmodifiableCollection(foreignKeys.values());
146 }
147
148
149
150
151
152
153
154
155 public void addCheckConstraint(String constraintName, String text) {
156 checkConstraints.put(constraintName, text);
157 }
158
159
160
161
162 protected void addForeignKey(String fkName, String fkColName,
163 String pkTableSchema, String pkTableName, String pkColName,
164 int updateRule, int deleteRule,
165 Map<String, Table> tables,
166 Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
167 if (fkName == null)
168 return;
169
170 ForeignKeyConstraint foreignKey = foreignKeys.get(fkName);
171
172 if (foreignKey == null) {
173 foreignKey = new ForeignKeyConstraint(this, fkName, updateRule, deleteRule);
174
175 foreignKeys.put(fkName, foreignKey);
176 }
177
178 TableColumn childColumn = getColumn(fkColName);
179 if (childColumn != null) {
180 foreignKey.addChildColumn(childColumn);
181
182 Table parentTable = tables.get(pkTableName);
183 String parentSchema = pkTableSchema;
184 String baseSchema = Config.getInstance().getSchema();
185
186
187
188 if (parentTable == null ||
189 (baseSchema != null && parentSchema != null &&
190 !baseSchema.equals(parentSchema))) {
191 parentTable = db.addRemoteTable(parentSchema, pkTableName, baseSchema,
192 properties, excludeIndirectColumns, excludeColumns);
193 }
194
195 if (parentTable != null) {
196 TableColumn parentColumn = parentTable.getColumn(pkColName);
197 if (parentColumn != null) {
198 foreignKey.addParentColumn(parentColumn);
199
200 childColumn.addParent(parentColumn, foreignKey);
201 parentColumn.addChild(childColumn, foreignKey);
202 } else {
203 logger.warning("Couldn't add FK '" + foreignKey.getName() + "' to table '" + this +
204 "' - Column '" + pkColName + "' doesn't exist in table '" + parentTable + "'");
205 }
206 } else {
207 logger.warning("Couldn't add FK '" + foreignKey.getName() + "' to table '" + this +
208 "' - Unknown Referenced Table '" + pkTableName + "'");
209 }
210 } else {
211 logger.warning("Couldn't add FK '" + foreignKey.getName() + "' to table '" + this +
212 "' - Column '" + fkColName + "' doesn't exist");
213 }
214 }
215
216
217
218
219
220 private void initPrimaryKeys(DatabaseMetaData meta) throws SQLException {
221 if (properties == null)
222 return;
223
224 ResultSet rs = null;
225
226 try {
227 rs = meta.getPrimaryKeys(null, getSchema(), getName());
228
229 while (rs.next())
230 setPrimaryColumn(rs);
231 } finally {
232 if (rs != null)
233 rs.close();
234 }
235 }
236
237
238
239
240
241 private void setPrimaryColumn(ResultSet rs) throws SQLException {
242 String pkName = rs.getString("PK_NAME");
243 if (pkName == null)
244 return;
245
246 TableIndex index = getIndex(pkName);
247 if (index != null) {
248 index.setIsPrimaryKey(true);
249 }
250
251 String columnName = rs.getString("COLUMN_NAME");
252
253 setPrimaryColumn(getColumn(columnName));
254 }
255
256
257
258
259 void setPrimaryColumn(TableColumn primaryColumn) {
260 primaryKeys.add(primaryColumn);
261 }
262
263
264
265
266
267
268 private void initColumns(Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
269 ResultSet rs = null;
270
271 synchronized (Table.class) {
272 try {
273 rs = db.getMetaData().getColumns(null, getSchema(), getName(), "%");
274
275 while (rs.next())
276 addColumn(rs, excludeIndirectColumns, excludeColumns);
277 } catch (SQLException exc) {
278 class ColumnInitializationFailure extends SQLException {
279 private static final long serialVersionUID = 1L;
280
281 public ColumnInitializationFailure(SQLException failure) {
282 super("Failed to collect column details for " + (isView() ? "view" : "table") + " '" + getName() + "' in schema '" + getSchema() + "'");
283 initCause(failure);
284 }
285 }
286
287 throw new ColumnInitializationFailure(exc);
288 } finally {
289 if (rs != null)
290 rs.close();
291 }
292 }
293
294 if (!isView() && !isRemote())
295 initColumnAutoUpdate(false);
296 }
297
298
299
300
301
302 private void initColumnAutoUpdate(boolean forceQuotes) throws SQLException {
303 ResultSet rs = null;
304 PreparedStatement stmt = null;
305
306
307
308
309 StringBuilder sql = new StringBuilder("select * from ");
310 if (getSchema() != null) {
311 sql.append(getSchema());
312 sql.append('.');
313 }
314
315 if (forceQuotes) {
316 String quote = db.getMetaData().getIdentifierQuoteString().trim();
317 sql.append(quote + getName() + quote);
318 } else
319 sql.append(db.getQuotedIdentifier(getName()));
320
321 sql.append(" where 0 = 1");
322
323 try {
324 stmt = db.getMetaData().getConnection().prepareStatement(sql.toString());
325 rs = stmt.executeQuery();
326
327 ResultSetMetaData rsMeta = rs.getMetaData();
328 for (int i = rsMeta.getColumnCount(); i > 0; --i) {
329 TableColumn column = getColumn(rsMeta.getColumnName(i));
330 column.setIsAutoUpdated(rsMeta.isAutoIncrement(i));
331 }
332 } catch (SQLException exc) {
333 if (forceQuotes) {
334
335 logger.warning("Failed to determine auto increment status: " + exc);
336 logger.warning("SQL: " + sql.toString());
337 } else {
338 initColumnAutoUpdate(true);
339 }
340 } finally {
341 if (rs != null)
342 rs.close();
343 if (stmt != null)
344 stmt.close();
345 }
346 }
347
348
349
350
351
352
353
354 protected void addColumn(ResultSet rs, Pattern excludeIndirectColumns, Pattern excludeColumns) throws SQLException {
355 String columnName = rs.getString("COLUMN_NAME");
356
357 if (columnName == null)
358 return;
359
360 if (getColumn(columnName) == null) {
361 TableColumn column = new TableColumn(this, rs, excludeIndirectColumns, excludeColumns);
362
363 columns.put(column.getName(), column);
364 }
365 }
366
367
368
369
370
371
372
373 protected TableColumn addColumn(TableColumnMeta colMeta) {
374 TableColumn column = new TableColumn(this, colMeta);
375
376 columns.put(column.getName(), column);
377
378 return column;
379 }
380
381
382
383
384
385
386 private void initIndexes() throws SQLException {
387 if (isView() || isRemote())
388 return;
389
390
391
392
393 if (initIndexes(properties.getProperty("selectIndexesSql")))
394 return;
395
396
397 ResultSet rs = null;
398
399 try {
400 rs = db.getMetaData().getIndexInfo(null, getSchema(), getName(), false, true);
401
402 while (rs.next()) {
403 if (rs.getShort("TYPE") != DatabaseMetaData.tableIndexStatistic)
404 addIndex(rs);
405 }
406 } catch (SQLException exc) {
407 logger.warning("Unable to extract index info for table '" + getName() + "' in schema '" + getSchema() + "': " + exc);
408 } finally {
409 if (rs != null)
410 rs.close();
411 }
412 }
413
414
415
416
417
418
419 private boolean initIndexes(String selectIndexesSql) {
420 if (selectIndexesSql == null)
421 return false;
422
423 PreparedStatement stmt = null;
424 ResultSet rs = null;
425
426 try {
427 stmt = db.prepareStatement(selectIndexesSql, getName());
428 rs = stmt.executeQuery();
429
430 while (rs.next()) {
431 if (rs.getShort("TYPE") != DatabaseMetaData.tableIndexStatistic)
432 addIndex(rs);
433 }
434 } catch (SQLException sqlException) {
435 logger.warning("Failed to query index information with SQL: " + selectIndexesSql);
436 logger.warning(sqlException.toString());
437 return false;
438 } finally {
439 if (rs != null) {
440 try {
441 rs.close();
442 } catch (Exception exc) {
443 exc.printStackTrace();
444 }
445 }
446 if (stmt != null) {
447 try {
448 stmt.close();
449 } catch (Exception exc) {
450 exc.printStackTrace();
451 }
452 }
453 }
454
455 return true;
456 }
457
458
459
460
461
462 public TableIndex getIndex(String indexName) {
463 return indexes.get(indexName);
464 }
465
466
467
468
469
470 private void addIndex(ResultSet rs) throws SQLException {
471 String indexName = rs.getString("INDEX_NAME");
472
473 if (indexName == null)
474 return;
475
476 TableIndex index = getIndex(indexName);
477
478 if (index == null) {
479 index = new TableIndex(rs);
480
481 indexes.put(index.getName(), index);
482 }
483
484 index.addColumn(getColumn(rs.getString("COLUMN_NAME")), rs.getString("ASC_OR_DESC"));
485 }
486
487
488
489
490
491
492 public String getSchema() {
493 return schema;
494 }
495
496
497
498
499
500
501 public String getName() {
502 return name;
503 }
504
505
506
507
508
509
510
511 public void setId(Object id) {
512 this.id = id;
513 }
514
515
516
517
518
519
520 public Object getId() {
521 return id;
522 }
523
524
525
526
527
528
529 public Map<String, String> getCheckConstraints() {
530 return checkConstraints;
531 }
532
533
534
535
536
537
538 public Set<TableIndex> getIndexes() {
539 return new HashSet<TableIndex>(indexes.values());
540 }
541
542
543
544
545
546
547 public List<TableColumn> getPrimaryColumns() {
548 return primaryKeys;
549 }
550
551
552
553
554 public String getComments() {
555 return comments;
556 }
557
558
559
560
561
562
563 public void setComments(String comments) {
564 String cmts = (comments == null || comments.trim().length() == 0) ? null : comments.trim();
565
566
567
568
569
570 if (cmts != null) {
571 int crapIndex = cmts.indexOf("; InnoDB free: ");
572 if (crapIndex == -1)
573 crapIndex = cmts.startsWith("InnoDB free: ") ? 0 : -1;
574 if (crapIndex != -1) {
575 cmts = cmts.substring(0, crapIndex).trim();
576 cmts = cmts.length() == 0 ? null : cmts;
577 }
578 }
579
580 this.comments = cmts;
581 }
582
583
584
585
586
587
588
589
590 public TableColumn getColumn(String columnName) {
591 return columns.get(columnName);
592 }
593
594
595
596
597
598
599 public List<TableColumn> getColumns() {
600 Set<TableColumn> sorted = new TreeSet<TableColumn>(new ByColumnIdComparator());
601 sorted.addAll(columns.values());
602 return new ArrayList<TableColumn>(sorted);
603 }
604
605
606
607
608
609
610 public boolean isRoot() {
611 for (TableColumn column : columns.values()) {
612 if (column.isForeignKey()) {
613 return false;
614 }
615 }
616
617 return true;
618 }
619
620
621
622
623
624
625 public boolean isLeaf() {
626 for (TableColumn column : columns.values()) {
627 if (!column.getChildren().isEmpty()) {
628 return false;
629 }
630 }
631
632 return true;
633 }
634
635
636
637
638
639
640
641 public int getMaxParents() {
642 return maxParents;
643 }
644
645
646
647
648
649 public void addedParent() {
650 maxParents++;
651 }
652
653
654
655
656 public void unlinkParents() {
657 for (TableColumn column : columns.values()) {
658 column.unlinkParents();
659 }
660 }
661
662
663
664
665
666
667
668 public int getMaxChildren() {
669 return maxChildren;
670 }
671
672
673
674
675
676 public void addedChild() {
677 maxChildren++;
678 }
679
680
681
682
683 public void unlinkChildren() {
684 for (TableColumn column : columns.values()) {
685 column.unlinkChildren();
686 }
687 }
688
689
690
691
692
693
694 public ForeignKeyConstraint removeSelfReferencingConstraint() {
695 return remove(getSelfReferencingConstraint());
696 }
697
698
699
700
701
702
703
704
705 private ForeignKeyConstraint remove(ForeignKeyConstraint constraint) {
706 if (constraint != null) {
707 for (int i = 0; i < constraint.getChildColumns().size(); i++) {
708 TableColumn childColumn = constraint.getChildColumns().get(i);
709 TableColumn parentColumn = constraint.getParentColumns().get(i);
710 childColumn.removeParent(parentColumn);
711 parentColumn.removeChild(childColumn);
712 }
713 }
714 return constraint;
715 }
716
717
718
719
720
721
722 private ForeignKeyConstraint getSelfReferencingConstraint() {
723 for (TableColumn column : columns.values()) {
724 for (TableColumn parentColumn : column.getParents()) {
725 if (compareTo(parentColumn.getTable()) == 0) {
726 return column.getParentConstraint(parentColumn);
727 }
728 }
729 }
730 return null;
731 }
732
733
734
735
736
737
738 public List<ForeignKeyConstraint> removeNonRealForeignKeys() {
739 List<ForeignKeyConstraint> nonReals = new ArrayList<ForeignKeyConstraint>();
740
741 for (TableColumn column : columns.values()) {
742 for (TableColumn parentColumn : column.getParents()) {
743 ForeignKeyConstraint constraint = column.getParentConstraint(parentColumn);
744 if (constraint != null && !constraint.isReal()) {
745 nonReals.add(constraint);
746 }
747 }
748 }
749
750
751
752 for (ForeignKeyConstraint constraint : nonReals) {
753 remove(constraint);
754 }
755
756 return nonReals;
757 }
758
759
760
761
762
763
764 public int getNumChildren() {
765 int numChildren = 0;
766
767 for (TableColumn column : columns.values()) {
768 numChildren += column.getChildren().size();
769 }
770
771 return numChildren;
772 }
773
774
775
776
777
778 public int getNumNonImpliedChildren() {
779 int numChildren = 0;
780
781 for (TableColumn column : columns.values()) {
782 for (TableColumn childColumn : column.getChildren()) {
783 if (!column.getChildConstraint(childColumn).isImplied())
784 ++numChildren;
785 }
786 }
787
788 return numChildren;
789 }
790
791
792
793
794
795
796 public int getNumParents() {
797 int numParents = 0;
798
799 for (TableColumn column : columns.values()) {
800 numParents += column.getParents().size();
801 }
802
803 return numParents;
804 }
805
806
807
808
809
810
811 public int getNumNonImpliedParents() {
812 int numParents = 0;
813
814 for (TableColumn column : columns.values()) {
815 for (TableColumn parentColumn : column.getParents()) {
816 if (!column.getParentConstraint(parentColumn).isImplied())
817 ++numParents;
818 }
819 }
820
821 return numParents;
822 }
823
824
825
826
827
828
829
830
831 public ForeignKeyConstraint removeAForeignKeyConstraint() {
832 @SuppressWarnings("hiding")
833 final List<TableColumn> columns = getColumns();
834 int numParents = 0;
835 int numChildren = 0;
836
837
838
839 for (TableColumn column : columns) {
840 numParents += column.getParents().size();
841 numChildren += column.getChildren().size();
842 }
843
844 for (TableColumn column : columns) {
845 ForeignKeyConstraint constraint;
846 if (numParents <= numChildren)
847 constraint = column.removeAParentFKConstraint();
848 else
849 constraint = column.removeAChildFKConstraint();
850 if (constraint != null)
851 return constraint;
852 }
853
854 return null;
855 }
856
857
858
859
860
861
862 public boolean isView() {
863 return false;
864 }
865
866
867
868
869
870 public boolean isRemote() {
871 return false;
872 }
873
874
875
876
877
878
879
880 public String getViewSql() {
881 return null;
882 }
883
884
885
886
887
888
889
890 public long getNumRows() {
891 if (numRows == null) {
892 numRows = Config.getInstance().isNumRowsEnabled() ? fetchNumRows() : -1;
893 }
894
895 return numRows;
896 }
897
898
899
900
901
902
903 public void setNumRows(long numRows) {
904 this.numRows = numRows;
905 }
906
907
908
909
910
911
912
913
914
915 protected long fetchNumRows() {
916 if (properties == null)
917 return 0;
918
919 SQLException originalFailure = null;
920
921 String sql = properties.getProperty("selectRowCountSql");
922 if (sql != null) {
923 PreparedStatement stmt = null;
924 ResultSet rs = null;
925
926 try {
927 stmt = db.prepareStatement(sql, getName());
928 rs = stmt.executeQuery();
929
930 while (rs.next()) {
931 return rs.getLong("row_count");
932 }
933 } catch (SQLException sqlException) {
934
935 originalFailure = sqlException;
936 } finally {
937 if (rs != null) {
938 try {
939 rs.close();
940 } catch (SQLException exc) {}
941 }
942 if (stmt != null) {
943 try {
944 stmt.close();
945 } catch (SQLException exc) {}
946 }
947 }
948 }
949
950
951 try {
952
953 return fetchNumRows("count(*)", false);
954 } catch (SQLException try2Exception) {
955 try {
956
957 return fetchNumRows("count(1)", false);
958 } catch (SQLException try3Exception) {
959 logger.warning("Unable to extract the number of rows for table " + getName() + ", using '-1'");
960 if (originalFailure != null)
961 logger.warning(originalFailure.toString());
962 logger.warning(try2Exception.toString());
963 logger.warning(try3Exception.toString());
964 return -1;
965 }
966 }
967 }
968
969 protected long fetchNumRows(String clause, boolean forceQuotes) throws SQLException {
970 PreparedStatement stmt = null;
971 ResultSet rs = null;
972 StringBuilder sql = new StringBuilder("select ");
973 sql.append(clause);
974 sql.append(" from ");
975 if (getSchema() != null) {
976 sql.append(getSchema());
977 sql.append('.');
978 }
979
980 if (forceQuotes) {
981 String quote = db.getMetaData().getIdentifierQuoteString().trim();
982 sql.append(quote + getName() + quote);
983 } else
984 sql.append(db.getQuotedIdentifier(getName()));
985
986 try {
987 stmt = db.getConnection().prepareStatement(sql.toString());
988 rs = stmt.executeQuery();
989 while (rs.next()) {
990 return rs.getLong(1);
991 }
992 return -1;
993 } catch (SQLException exc) {
994 if (forceQuotes)
995 throw exc;
996
997 return fetchNumRows(clause, true);
998 } finally {
999 if (rs != null)
1000 rs.close();
1001 if (stmt != null)
1002 stmt.close();
1003 }
1004 }
1005
1006
1007
1008
1009
1010
1011 public void update(TableMeta tableMeta) {
1012 String newComments = tableMeta.getComments();
1013 if (newComments != null) {
1014 comments = newComments;
1015 }
1016
1017 for (TableColumnMeta colMeta : tableMeta.getColumns()) {
1018 TableColumn col = getColumn(colMeta.getName());
1019 if (col == null) {
1020 if (tableMeta.getRemoteSchema() == null) {
1021 logger.warning("Unrecognized column '" + colMeta.getName() + "' for table '" + getName() + '\'');
1022 continue;
1023 }
1024
1025 col = addColumn(colMeta);
1026 }
1027
1028
1029 col.update(colMeta);
1030 }
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040 public void connect(TableMeta tableMeta, Map<String, Table> tables, Map<String, Table> remoteTables) {
1041 for (TableColumnMeta colMeta : tableMeta.getColumns()) {
1042 TableColumn col = getColumn(colMeta.getName());
1043
1044
1045 for (ForeignKeyMeta fk : colMeta.getForeignKeys()) {
1046 Table parent = fk.getRemoteSchema() == null ? tables.get(fk.getTableName())
1047 : remoteTables.get(fk.getRemoteSchema() + '.' + fk.getTableName());
1048 if (parent != null) {
1049 TableColumn parentColumn = parent.getColumn(fk.getColumnName());
1050
1051 if (parentColumn == null) {
1052 logger.warning(parent.getName() + '.' + fk.getColumnName() + " doesn't exist");
1053 } else {
1054
1055
1056
1057
1058 new ForeignKeyConstraint(parentColumn, col) {
1059 @Override
1060 public String getName() {
1061 return "Defined in XML";
1062 }
1063 };
1064 }
1065 } else {
1066 logger.warning("Undefined table '" + fk.getTableName() + "' referenced by '" + getName() + '.' + col.getName() + '\'');
1067 }
1068 }
1069 }
1070 }
1071
1072 @Override
1073 public String toString() {
1074 return getName();
1075 }
1076
1077
1078
1079
1080
1081
1082
1083 public boolean isOrphan(boolean withImpliedRelationships) {
1084 if (withImpliedRelationships)
1085 return getMaxParents() == 0 && getMaxChildren() == 0;
1086
1087 for (TableColumn column : columns.values()) {
1088 for (TableColumn parentColumn : column.getParents()) {
1089 if (!column.getParentConstraint(parentColumn).isImplied())
1090 return false;
1091 }
1092 for (TableColumn childColumn : column.getChildren()) {
1093 if (!column.getChildConstraint(childColumn).isImplied())
1094 return false;
1095 }
1096 }
1097 return true;
1098 }
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109 public int compareTo(Table other) {
1110 if (other == this)
1111 return 0;
1112
1113 int rc = getName().compareToIgnoreCase(other.getName());
1114 if (rc == 0) {
1115
1116 String ours = getSchema();
1117 String theirs = other.getSchema();
1118 if (ours != null && theirs != null)
1119 rc = ours.compareToIgnoreCase(theirs);
1120 else if (ours == null)
1121 rc = -1;
1122 else
1123 rc = 1;
1124 }
1125
1126 return rc;
1127 }
1128
1129
1130
1131
1132
1133
1134 private static class ByColumnIdComparator implements Comparator<TableColumn> {
1135 public int compare(TableColumn column1, TableColumn column2) {
1136 if (column1.getId() == null || column2.getId() == null)
1137 return column1.getName().compareToIgnoreCase(column2.getName());
1138 if (column1.getId() instanceof Number)
1139 return ((Number)column1.getId()).intValue() - ((Number)column2.getId()).intValue();
1140 return column1.getId().toString().compareToIgnoreCase(column2.getId().toString());
1141 }
1142 }
1143 }