001: /*
002: * ColumnDiff.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.db.diff;
013:
014: import workbench.db.ColumnIdentifier;
015: import workbench.db.report.ColumnReference;
016: import workbench.db.report.ReportColumn;
017: import workbench.db.report.TagWriter;
018: import workbench.util.SqlUtil;
019: import workbench.util.StrBuffer;
020: import workbench.util.StringUtil;
021:
022: /**
023: * Compare two table columns for possible differences.
024: * Currently the following attributes are checked:
025: <ul>
026: * <li>Data type (as returned from the database)</li>
027: * <li>NULL value allowed</li>
028: * <li>Default value</li>
029: * <li>Comments (if returned by the JDBC driver)</li>
030: * </ul>
031: * @author support@sql-workbench.net
032: */
033: public class ColumnDiff {
034: public static final String TAG_MODIFY_COLUMN = "modify-column";
035: public static final String TAG_DROP_FK = "drop-reference";
036: public static final String TAG_ADD_FK = "add-reference";
037:
038: // Use a ReportColumn for future FK reference diff...
039: private ReportColumn referenceColumn;
040: private ReportColumn targetColumn;
041: private StrBuffer indent;
042: private TagWriter writer;
043: private boolean compareFK = true;
044: private boolean compareComments = true;
045: private boolean compareJdbcTypes = false;
046:
047: /**
048: * Create a ColumnDiff object for the reference and target columns
049: */
050: public ColumnDiff(ReportColumn reference, ReportColumn target) {
051: if (reference == null)
052: throw new NullPointerException(
053: "Reference column may not be null");
054: if (target == null)
055: throw new NullPointerException(
056: "Target column may not be null");
057: this .referenceColumn = reference;
058: this .targetColumn = target;
059: }
060:
061: public void setCompareForeignKeys(boolean flag) {
062: this .compareFK = flag;
063: }
064:
065: public void setCompareComments(boolean flag) {
066: this .compareComments = flag;
067: }
068:
069: /**
070: * Set the {@link workbench.db.report.TagWriter} to
071: * be used for writing the XML tags
072: */
073: public void setTagWriter(TagWriter tw) {
074: this .writer = tw;
075: }
076:
077: /**
078: * Set an indent for generating the XML
079: */
080: public void setIndent(String ind) {
081: if (ind == null)
082: this .indent = null;
083: else
084: this .indent = new StrBuffer(ind);
085: }
086:
087: /**
088: * Set an indent for generating the XML
089: */
090: public void setIndent(StrBuffer ind) {
091: this .indent = ind;
092: }
093:
094: private boolean typesAreEqual() {
095: ColumnIdentifier sId = this .referenceColumn.getColumn();
096: ColumnIdentifier tId = this .targetColumn.getColumn();
097: if (this .getCompareJdbcTypes()) {
098: int sourceType = sId.getDataType();
099: int targetType = tId.getDataType();
100: if (sourceType != targetType)
101: return false;
102:
103: if (SqlUtil.isCharacterType(sourceType)) {
104: int sourceSize = sId.getColumnSize();
105: int targetSize = tId.getColumnSize();
106: return targetSize == sourceSize;
107: } else if (SqlUtil.isNumberType(sourceType)) {
108: int sourceDigits = sId.getDecimalDigits();
109: int targetDigits = sId.getDecimalDigits();
110: return sourceDigits == targetDigits;
111: }
112: return true;
113: } else {
114: return sId.getDbmsType()
115: .equalsIgnoreCase(tId.getDbmsType());
116: }
117: }
118:
119: /**
120: * Return the XML describing how to modify the reference column
121: * to get the same definition as the source column.
122: * An empty string means that there are no differences
123: * This does not include foreign key references.
124: */
125: public StrBuffer getMigrateTargetXml() {
126: ColumnIdentifier sId = this .referenceColumn.getColumn();
127: ColumnIdentifier tId = this .targetColumn.getColumn();
128: StrBuffer result = new StrBuffer();
129: StrBuffer myindent = new StrBuffer(this .indent);
130: myindent.append(" ");
131:
132: // the PK attribute is not checked, because this is already handled
133: // by the TableDiff class
134: boolean typeDifferent = !typesAreEqual();
135: boolean nullableDifferent = (sId.isNullable() != tId
136: .isNullable());
137: String sdef = sId.getDefaultValue();
138: String tdef = tId.getDefaultValue();
139: boolean defaultDifferent = !StringUtil.equalString(sdef, tdef);
140:
141: ColumnReference refFk = this .referenceColumn.getForeignKey();
142: ColumnReference targetFk = this .targetColumn.getForeignKey();
143: boolean fkDifferent = false;
144: if (this .compareFK) {
145: if (refFk == null && targetFk == null) {
146: fkDifferent = false;
147: } else if ((refFk == null && targetFk != null)
148: || (refFk != null && targetFk == null)) {
149: fkDifferent = true;
150: } else {
151: fkDifferent = !(refFk.equals(targetFk));
152: }
153: }
154:
155: String scomm = sId.getComment();
156: String tcomm = tId.getComment();
157: boolean commentDifferent = false;
158: if (this .compareComments) {
159: commentDifferent = !StringUtil.equalString(scomm, tcomm);
160: }
161:
162: if (writer == null)
163: this .writer = new TagWriter();
164:
165: if (typeDifferent || nullableDifferent || defaultDifferent
166: || commentDifferent || fkDifferent) {
167: writer.appendOpenTag(result, this .indent,
168: TAG_MODIFY_COLUMN, "name", tId.getColumnName());
169: result.append('\n');
170: if (typeDifferent) {
171: writer.appendTag(result, myindent,
172: ReportColumn.TAG_COLUMN_DBMS_TYPE, sId
173: .getDbmsType());
174: writer.appendTag(result, myindent,
175: ReportColumn.TAG_COLUMN_SIZE, sId
176: .getColumnSize());
177: writer.appendTag(result, myindent,
178: ReportColumn.TAG_COLUMN_DIGITS, sId
179: .getDecimalDigits());
180: writer.appendTag(result, myindent,
181: ReportColumn.TAG_COLUMN_JAVA_TYPE_NAME, sId
182: .getColumnTypeName());
183: }
184: if (nullableDifferent) {
185: writer.appendTag(result, myindent,
186: ReportColumn.TAG_COLUMN_NULLABLE, sId
187: .isNullable());
188: }
189: if (defaultDifferent) {
190: writer.appendTag(result, myindent,
191: ReportColumn.TAG_COLUMN_DEFAULT,
192: (sdef == null ? "" : sdef));
193: }
194: if (commentDifferent) {
195: writer.appendTag(result, myindent,
196: ReportColumn.TAG_COLUMN_COMMENT,
197: (scomm == null ? "" : scomm));
198: }
199: if (fkDifferent) {
200: StrBuffer refIndent = new StrBuffer(myindent);
201: refIndent.append(" ");
202: if (refFk == null) {
203: writer.appendOpenTag(result, myindent, TAG_DROP_FK);
204: result.append('\n');
205: result.append(targetFk.getInnerXml(refIndent));
206: writer
207: .appendCloseTag(result, myindent,
208: TAG_DROP_FK);
209: } else {
210: writer.appendOpenTag(result, myindent, TAG_ADD_FK);
211: result.append('\n');
212: result.append(refFk.getInnerXml(refIndent));
213: writer.appendCloseTag(result, myindent, TAG_ADD_FK);
214: }
215: }
216: writer.appendCloseTag(result, this .indent,
217: TAG_MODIFY_COLUMN);
218: }
219: return result;
220: }
221:
222: public boolean getCompareJdbcTypes() {
223: return compareJdbcTypes;
224: }
225:
226: public void setCompareJdbcTypes(boolean compareJdbcTypes) {
227: this.compareJdbcTypes = compareJdbcTypes;
228: }
229: }
|