001: package liquibase.parser.xml;
002:
003: import liquibase.ChangeSet;
004: import liquibase.DatabaseChangeLog;
005: import liquibase.FileOpener;
006: import liquibase.change.*;
007: import liquibase.change.custom.CustomChangeWrapper;
008: import liquibase.exception.CustomChangeException;
009: import liquibase.exception.LiquibaseException;
010: import liquibase.exception.MigrationFailedException;
011: import liquibase.log.LogFactory;
012: import liquibase.parser.ChangeLogParser;
013: import liquibase.preconditions.*;
014: import liquibase.util.ObjectUtil;
015: import liquibase.util.StringUtils;
016: import org.xml.sax.Attributes;
017: import org.xml.sax.SAXException;
018: import org.xml.sax.helpers.DefaultHandler;
019:
020: import java.lang.reflect.InvocationTargetException;
021: import java.util.List;
022: import java.util.Stack;
023: import java.util.logging.Level;
024: import java.util.logging.Logger;
025:
026: class XMLChangeLogHandler extends DefaultHandler {
027:
028: protected Logger log;
029:
030: private DatabaseChangeLog databaseChangeLog;
031: private Change change;
032: private StringBuffer text;
033: private AndPrecondition rootPrecondition;
034: private Stack<PreconditionLogic> preconditionLogicStack = new Stack<PreconditionLogic>();
035: private ChangeSet changeSet;
036: private FileOpener fileOpener;
037: private Precondition currentPrecondition;
038:
039: protected XMLChangeLogHandler(String physicalChangeLogLocation,
040: FileOpener fileOpener) {
041: log = LogFactory.getLogger();
042: this .fileOpener = fileOpener;
043:
044: databaseChangeLog = new DatabaseChangeLog(
045: physicalChangeLogLocation);
046: databaseChangeLog
047: .setPhysicalFilePath(physicalChangeLogLocation);
048: }
049:
050: public DatabaseChangeLog getDatabaseChangeLog() {
051: return databaseChangeLog;
052: }
053:
054: public void startElement(String uri, String localName,
055: String qName, Attributes atts) throws SAXException {
056: try {
057: if ("comment".equals(qName)) {
058: text = new StringBuffer();
059: } else if ("databaseChangeLog".equals(qName)) {
060: String version = uri
061: .substring(uri.lastIndexOf("/") + 1);
062: if (!version.equals(XMLChangeLogParser
063: .getSchemaVersion())) {
064: log.warning(databaseChangeLog.getPhysicalFilePath()
065: + " is using schema version " + version
066: + " rather than version "
067: + XMLChangeLogParser.getSchemaVersion());
068: }
069: databaseChangeLog.setLogicalFilePath(atts
070: .getValue("logicalFilePath"));
071: } else if ("include".equals(qName)) {
072: String fileName = atts.getValue("file");
073: handleIncludedChangeLog(fileName);
074: } else if (changeSet == null && "changeSet".equals(qName)) {
075: boolean alwaysRun = false;
076: boolean runOnChange = false;
077: if ("true".equalsIgnoreCase(atts.getValue("runAlways"))) {
078: alwaysRun = true;
079: }
080: if ("true".equalsIgnoreCase(atts
081: .getValue("runOnChange"))) {
082: runOnChange = true;
083: }
084: changeSet = new ChangeSet(atts.getValue("id"), atts
085: .getValue("author"), alwaysRun, runOnChange,
086: databaseChangeLog.getFilePath(),
087: databaseChangeLog.getPhysicalFilePath(), atts
088: .getValue("context"), atts
089: .getValue("dbms"));
090: if (StringUtils
091: .trimToNull(atts.getValue("failOnError")) != null) {
092: changeSet.setFailOnError(Boolean.parseBoolean(atts
093: .getValue("failOnError")));
094: }
095: } else if (changeSet != null && "rollback".equals(qName)) {
096: text = new StringBuffer();
097: } else if (changeSet != null && change == null) {
098: change = ChangeFactory.getInstance().create(qName);
099: change.setChangeSet(changeSet);
100: text = new StringBuffer();
101: if (change == null) {
102: throw new MigrationFailedException(changeSet,
103: "Unknown change: " + qName);
104: }
105: change.setFileOpener(fileOpener);
106: if (change instanceof CustomChangeWrapper) {
107: ((CustomChangeWrapper) change)
108: .setClassLoader(fileOpener.toClassLoader());
109: }
110: for (int i = 0; i < atts.getLength(); i++) {
111: String attributeName = atts.getQName(i);
112: String attributeValue = atts.getValue(i);
113: setProperty(change, attributeName, attributeValue);
114: }
115: change.setUp();
116: } else if (change != null && "column".equals(qName)) {
117: ColumnConfig column = new ColumnConfig();
118: for (int i = 0; i < atts.getLength(); i++) {
119: String attributeName = atts.getQName(i);
120: String attributeValue = atts.getValue(i);
121: setProperty(column, attributeName, attributeValue);
122: }
123: if (change instanceof AddColumnChange) {
124: ((AddColumnChange) change).addColumn(column);
125: } else if (change instanceof CreateTableChange) {
126: ((CreateTableChange) change).addColumn(column);
127: } else if (change instanceof InsertDataChange) {
128: ((InsertDataChange) change).addColumn(column);
129: } else if (change instanceof UpdateDataChange) {
130: ((UpdateDataChange) change).addColumn(column);
131: } else if (change instanceof CreateIndexChange) {
132: ((CreateIndexChange) change).addColumn(column);
133: } else if (change instanceof ModifyColumnChange) {
134: ((ModifyColumnChange) change).addColumn(column);
135: } else {
136: throw new RuntimeException(
137: "Unexpected column tag for "
138: + change.getClass().getName());
139: }
140: } else if (change != null && "constraints".equals(qName)) {
141: ConstraintsConfig constraints = new ConstraintsConfig();
142: for (int i = 0; i < atts.getLength(); i++) {
143: String attributeName = atts.getQName(i);
144: String attributeValue = atts.getValue(i);
145: setProperty(constraints, attributeName,
146: attributeValue);
147: }
148: ColumnConfig lastColumn;
149: if (change instanceof AddColumnChange) {
150: lastColumn = ((AddColumnChange) change)
151: .getLastColumn();
152: } else if (change instanceof CreateTableChange) {
153: lastColumn = ((CreateTableChange) change)
154: .getColumns().get(
155: ((CreateTableChange) change)
156: .getColumns().size() - 1);
157: } else {
158: throw new RuntimeException("Unexpected change: "
159: + change.getClass().getName());
160: }
161: lastColumn.setConstraints(constraints);
162: } else if ("preConditions".equals(qName)) {
163: rootPrecondition = new AndPrecondition();
164: preconditionLogicStack.push(rootPrecondition);
165:
166: } else if (rootPrecondition != null) {
167: currentPrecondition = PreconditionFactory.getInstance()
168: .create(qName);
169:
170: for (int i = 0; i < atts.getLength(); i++) {
171: String attributeName = atts.getQName(i);
172: String attributeValue = atts.getValue(i);
173: setProperty(currentPrecondition, attributeName,
174: attributeValue);
175: }
176: preconditionLogicStack.peek().addNestedPrecondition(
177: currentPrecondition);
178:
179: if (currentPrecondition instanceof PreconditionLogic) {
180: preconditionLogicStack
181: .push(((PreconditionLogic) currentPrecondition));
182: }
183:
184: if ("sqlCheck".equals(qName)) {
185: text = new StringBuffer();
186: }
187: } else if ("param".equals(qName)) {
188: if (change instanceof CustomChangeWrapper) {
189: ((CustomChangeWrapper) change).setParam(atts
190: .getValue("name"), atts.getValue("value"));
191: } else {
192: throw new MigrationFailedException(changeSet,
193: "'param' unexpected in " + qName);
194: }
195: } else if ("where".equals(qName)) {
196: text = new StringBuffer();
197: } else if (change instanceof ExecuteShellCommandChange
198: && "arg".equals(qName)) {
199: ((ExecuteShellCommandChange) change).addArg(atts
200: .getValue("value"));
201: } else {
202: throw new MigrationFailedException(changeSet,
203: "Unexpected tag: " + qName);
204: }
205: } catch (Exception e) {
206: log.log(Level.SEVERE, "Error thrown as a SAXException: "
207: + e.getMessage(), e);
208: throw new SAXException(e);
209: }
210: }
211:
212: protected void handleIncludedChangeLog(String fileName)
213: throws LiquibaseException {
214: for (ChangeSet changeSet : new ChangeLogParser().parse(
215: fileName, fileOpener).getChangeSets()) {
216: databaseChangeLog.addChangeSet(changeSet);
217: }
218: }
219:
220: private void setProperty(Object object, String attributeName,
221: String attributeValue) throws IllegalAccessException,
222: InvocationTargetException, CustomChangeException {
223: if (object instanceof CustomChangeWrapper) {
224: if (attributeName.equals("class")) {
225: ((CustomChangeWrapper) object).setClass(attributeValue);
226: } else {
227: ((CustomChangeWrapper) object).setParam(attributeName,
228: attributeValue);
229: }
230: } else {
231: ObjectUtil.setProperty(object, attributeName,
232: attributeValue);
233: }
234: }
235:
236: public void endElement(String uri, String localName, String qName)
237: throws SAXException {
238: String textString = null;
239: if (text != null && text.length() > 0) {
240: textString = StringUtils.trimToNull(text.toString());
241: }
242:
243: try {
244: if (rootPrecondition != null) {
245: if ("preConditions".equals(qName)) {
246: databaseChangeLog
247: .setPreconditions(rootPrecondition);
248: handlePreCondition(rootPrecondition);
249: rootPrecondition = null;
250: } else if ("and".equals(qName)) {
251: preconditionLogicStack.pop();
252: currentPrecondition = null;
253: } else if ("or".equals(qName)) {
254: preconditionLogicStack.pop();
255: currentPrecondition = null;
256: } else if ("not".equals(qName)) {
257: preconditionLogicStack.pop();
258: currentPrecondition = null;
259: } else if (qName.equals("sqlCheck")) {
260: ((SqlPrecondition) currentPrecondition)
261: .setSql(textString);
262: currentPrecondition = null;
263: }
264:
265: } else if (changeSet != null && "rollback".equals(qName)) {
266: changeSet.setRollBackSQL(textString);
267: } else if (change != null && change instanceof RawSQLChange
268: && "comment".equals(qName)) {
269: ((RawSQLChange) change).setComments(textString);
270: text = new StringBuffer();
271: } else if (change != null && "where".equals(qName)) {
272: if (change instanceof UpdateDataChange) {
273: ((UpdateDataChange) change)
274: .setWhereClause(textString);
275: } else if (change instanceof DeleteDataChange) {
276: ((DeleteDataChange) change)
277: .setWhereClause(textString);
278: } else {
279: throw new RuntimeException(
280: "Unexpected change type: "
281: + change.getClass().getName());
282: }
283: text = new StringBuffer();
284: } else if (change != null
285: && change instanceof CreateProcedureChange
286: && "comment".equals(qName)) {
287: ((CreateProcedureChange) change)
288: .setComments(textString);
289: text = new StringBuffer();
290: } else if (changeSet != null && "comment".equals(qName)) {
291: changeSet.setComments(textString);
292: text = new StringBuffer();
293: } else if (changeSet != null && "changeSet".equals(qName)) {
294: handleChangeSet(changeSet);
295: changeSet = null;
296: } else if (change != null
297: && qName.equals(change.getTagName())) {
298: if (textString != null) {
299: if (change instanceof RawSQLChange) {
300: ((RawSQLChange) change).setSql(textString);
301: } else if (change instanceof CreateProcedureChange) {
302: ((CreateProcedureChange) change)
303: .setProcedureBody(textString);
304: } else if (change instanceof CreateViewChange) {
305: ((CreateViewChange) change)
306: .setSelectQuery(textString);
307: } else if (change instanceof InsertDataChange) {
308: List<ColumnConfig> columns = ((InsertDataChange) change)
309: .getColumns();
310: columns.get(columns.size() - 1).setValue(
311: textString);
312: } else if (change instanceof UpdateDataChange) {
313: List<ColumnConfig> columns = ((UpdateDataChange) change)
314: .getColumns();
315: columns.get(columns.size() - 1).setValue(
316: textString);
317: } else {
318: throw new RuntimeException(
319: "Unexpected text in "
320: + change.getTagName());
321: }
322: }
323: text = null;
324: changeSet.addChange(change);
325: change = null;
326: }
327: } catch (Exception e) {
328: log.log(Level.SEVERE, "Error thrown as a SAXException: "
329: + e.getMessage(), e);
330: throw new SAXException(databaseChangeLog
331: .getPhysicalFilePath()
332: + ": " + e.getMessage(), e);
333: }
334: }
335:
336: protected void handlePreCondition(@SuppressWarnings("unused")
337: Precondition precondition) {
338: databaseChangeLog.setPreconditions(rootPrecondition);
339: }
340:
341: protected void handleChangeSet(ChangeSet changeSet) {
342: databaseChangeLog.addChangeSet(changeSet);
343: }
344:
345: public void characters(char ch[], int start, int length)
346: throws SAXException {
347: if (text != null) {
348: text.append(new String(ch, start, length));
349: }
350: }
351: }
|