1 package com.rsmart.kuali.tools.liquibase;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.lang.reflect.InvocationTargetException;
7 import java.lang.reflect.Method;
8 import java.net.URL;
9 import java.net.URLDecoder;
10 import java.util.*;
11 import java.util.jar.JarEntry;
12 import java.util.jar.JarFile;
13 import java.util.regex.Matcher;
14 import java.util.regex.Pattern;
15
16 import liquibase.parser.core.xml.*;
17
18 import liquibase.change.Change;
19 import liquibase.change.ChangeFactory;
20 import liquibase.change.ChangeWithColumns;
21 import liquibase.change.ColumnConfig;
22 import liquibase.change.ConstraintsConfig;
23 import liquibase.change.core.CreateProcedureChange;
24 import liquibase.change.core.CreateViewChange;
25 import liquibase.change.core.DeleteDataChange;
26 import liquibase.change.core.ExecuteShellCommandChange;
27 import liquibase.change.core.InsertDataChange;
28 import liquibase.change.core.LoadDataChange;
29 import liquibase.change.core.LoadDataColumnConfig;
30 import liquibase.change.core.RawSQLChange;
31 import liquibase.change.core.StopChange;
32 import liquibase.change.core.UpdateDataChange;
33 import liquibase.change.custom.CustomChangeWrapper;
34 import liquibase.changelog.ChangeLogParameters;
35 import liquibase.changelog.ChangeSet;
36 import liquibase.changelog.DatabaseChangeLog;
37 import liquibase.exception.CustomChangeException;
38 import liquibase.exception.LiquibaseException;
39 import liquibase.exception.MigrationFailedException;
40 import liquibase.logging.LogFactory;
41 import liquibase.logging.Logger;
42 import liquibase.parser.ChangeLogParserFactory;
43 import liquibase.precondition.CustomPreconditionWrapper;
44 import liquibase.precondition.Precondition;
45 import liquibase.precondition.PreconditionFactory;
46 import liquibase.precondition.PreconditionLogic;
47 import liquibase.precondition.core.PreconditionContainer;
48 import liquibase.precondition.core.SqlPrecondition;
49 import liquibase.resource.ResourceAccessor;
50 import liquibase.sql.visitor.SqlVisitor;
51 import liquibase.sql.visitor.SqlVisitorFactory;
52 import liquibase.util.ObjectUtil;
53 import liquibase.util.StringUtils;
54 import liquibase.util.file.FilenameUtils;
55
56 import org.xml.sax.Attributes;
57 import org.xml.sax.SAXException;
58 import org.xml.sax.helpers.DefaultHandler;
59
60 class XMLChangeLogSAXHandler extends DefaultHandler {
61
62 private static final char LIQUIBASE_FILE_SEPARATOR = '/';
63
64 protected Logger log;
65
66 private DatabaseChangeLog databaseChangeLog;
67 private Change change;
68 private Stack changeSubObjects = new Stack();
69 private StringBuffer text;
70 private PreconditionContainer rootPrecondition;
71 private Stack<PreconditionLogic> preconditionLogicStack = new Stack<PreconditionLogic>();
72 private ChangeSet changeSet;
73 private String paramName;
74 private ResourceAccessor resourceAccessor;
75 private Precondition currentPrecondition;
76
77 private ChangeLogParameters changeLogParameters;
78 private boolean inRollback = false;
79
80 private boolean inModifySql = false;
81 private Set<String> modifySqlDbmsList;
82 private Set<String> modifySqlContexts;
83 private boolean modifySqlAppliedOnRollback = false;
84
85 protected XMLChangeLogSAXHandler(String physicalChangeLogLocation,
86 ResourceAccessor resourceAccessor,
87 ChangeLogParameters changeLogParameters) {
88 log = LogFactory.getLogger();
89 this.resourceAccessor = resourceAccessor;
90
91 databaseChangeLog = new DatabaseChangeLog();
92 databaseChangeLog.setPhysicalFilePath(physicalChangeLogLocation);
93 databaseChangeLog.setChangeLogParameters(changeLogParameters);
94
95 this.changeLogParameters = changeLogParameters;
96 }
97
98 public DatabaseChangeLog getDatabaseChangeLog() {
99 return databaseChangeLog;
100 }
101
102 @Override
103 public void startElement(String uri, String localName, String qName,
104 Attributes baseAttributes) throws SAXException {
105 Attributes atts = new ExpandingAttributes(baseAttributes);
106 try {
107 if ("comment".equals(qName)) {
108 text = new StringBuffer();
109 } else if ("validCheckSum".equals(qName)) {
110 text = new StringBuffer();
111 } else if ("databaseChangeLog".equals(qName)) {
112 String schemaLocation = atts.getValue("xsi:schemaLocation");
113 if (schemaLocation != null) {
114 Matcher matcher = Pattern.compile(
115 ".*dbchangelog-(\\d+\\.\\d+).xsd").matcher(
116 schemaLocation);
117 if (matcher.matches()) {
118 String version = matcher.group(1);
119 if (!version.equals(XMLChangeLogSAXParser
120 .getSchemaVersion())) {
121 log.info(databaseChangeLog.getPhysicalFilePath()
122 + " is using schema version " + version
123 + " rather than version "
124 + XMLChangeLogSAXParser.getSchemaVersion());
125 }
126 }
127 }
128 databaseChangeLog.setLogicalFilePath(atts
129 .getValue("logicalFilePath"));
130 } else if ("include".equals(qName)) {
131 String fileName = atts.getValue("file");
132 fileName = fileName.replace('\\', '/');
133 boolean isRelativeToChangelogFile = Boolean.parseBoolean(atts
134 .getValue("relativeToChangelogFile"));
135 handleIncludedChangeLog(fileName, isRelativeToChangelogFile,
136 databaseChangeLog.getPhysicalFilePath());
137 } else if ("includeAll".equals(qName)) {
138 String pathName = atts.getValue("path");
139 pathName = pathName.replace('\\', '/');
140
141 if (!(pathName.endsWith("/"))) {
142 pathName = pathName + '/';
143 }
144 log.debug("includeAll for " + pathName);
145 log.debug("Using file opener for includeAll: " + resourceAccessor.toString());
146 boolean isRelativeToChangelogFile = Boolean.parseBoolean(atts
147 .getValue("relativeToChangelogFile"));
148
149 if (isRelativeToChangelogFile) {
150 File changeLogFile = new File(databaseChangeLog
151 .getPhysicalFilePath());
152 File resourceBase = new File(changeLogFile.getParent(),
153 pathName);
154 if (!resourceBase.exists()) {
155 throw new SAXException(
156 "Resource directory for includeAll does not exist ["
157 + resourceBase.getPath() + "]");
158 }
159 pathName = resourceBase.getPath() + '/';
160 pathName = pathName.replace('\\', '/');
161 }
162
163 Enumeration<URL> resourcesEnum = resourceAccessor.getResources(pathName);
164 SortedSet<URL> resources = new TreeSet<URL>(new Comparator<URL>() {
165 public int compare(URL o1, URL o2) {
166 return o1.toString().compareTo(o2.toString());
167 }
168 });
169 while (resourcesEnum.hasMoreElements()) {
170 resources.add(resourcesEnum.nextElement());
171 }
172
173 boolean foundResource = false;
174
175 Set<String> seenPaths = new HashSet<String>();
176 for (URL fileUrl : resources) {
177 if (!fileUrl.toExternalForm().startsWith("file:")) {
178 if (fileUrl.toExternalForm().startsWith("jar:file:") || fileUrl.toExternalForm().startsWith("wsjar:file:") || fileUrl.toExternalForm().startsWith("zip:")) {
179 File zipFileDir = extractZipFile(fileUrl);
180 fileUrl = new File(zipFileDir, pathName).toURL();
181 } else {
182 log.debug(fileUrl.toExternalForm()
183 + " is not a file path");
184 continue;
185 }
186 }
187 File file = new File(fileUrl.toURI());
188 log.debug("includeAll using path "
189 + file.getCanonicalPath());
190 if (!file.exists()) {
191 throw new SAXException("includeAll path " + pathName
192 + " could not be found. Tried in "
193 + file.toString());
194 }
195 if (file.isDirectory()) {
196 log.debug(file.getCanonicalPath() + " is a directory");
197 for (File childFile : new TreeSet<File>(Arrays.asList(file.listFiles()))) {
198 String path = pathName+ childFile.getName();
199 if (!seenPaths.add(path)) {
200 log.debug("already included "+path);
201 continue;
202 }
203
204 if (handleIncludedChangeLog(path, false, databaseChangeLog.getPhysicalFilePath())) {
205 foundResource = true;
206 }
207 }
208 } else {
209 String path = pathName + file.getName();
210 if (!seenPaths.add(path)) {
211 log.debug("already included "+path);
212 continue;
213 }
214 if (handleIncludedChangeLog(path, false, databaseChangeLog.getPhysicalFilePath())) {
215 foundResource = true;
216 }
217 }
218 }
219
220 if (!foundResource) {
221 throw new SAXException( "Could not find directory or directory was empty for includeAll '" + pathName + "'");
222 }
223 } else if (changeSet == null && "changeSet".equals(qName)) {
224 boolean alwaysRun = false;
225 boolean runOnChange = false;
226 if ("true".equalsIgnoreCase(atts.getValue("runAlways"))) {
227 alwaysRun = true;
228 }
229 if ("true".equalsIgnoreCase(atts.getValue("runOnChange"))) {
230 runOnChange = true;
231 }
232 String filePath = atts.getValue("logicalFilePath");
233 if (filePath == null || "".equals(filePath)) {
234 filePath = databaseChangeLog.getFilePath();
235 }
236
237 changeSet = new ChangeSet(atts.getValue("id"), atts.getValue("author"), alwaysRun, runOnChange, filePath,
238 atts.getValue("context"), atts.getValue("dbms"),
239 Boolean.valueOf(atts.getValue("runInTransaction")));
240 if (StringUtils.trimToNull(atts.getValue("failOnError")) != null) {
241 changeSet.setFailOnError(Boolean.parseBoolean(atts.getValue("failOnError")));
242 }
243 if (StringUtils.trimToNull(atts.getValue("onValidationFail")) != null) {
244 changeSet.setOnValidationFail(ChangeSet.ValidationFailOption.valueOf(atts.getValue("onValidationFail")));
245 }
246 } else if (changeSet != null && "rollback".equals(qName)) {
247 text = new StringBuffer();
248 String id = atts.getValue("changeSetId");
249 if (id != null) {
250 String path = atts.getValue("changeSetPath");
251 if (path == null) {
252 path = databaseChangeLog.getFilePath();
253 }
254 String author = atts.getValue("changeSetAuthor");
255 ChangeSet changeSet = databaseChangeLog.getChangeSet(path,
256 author, id);
257 if (changeSet == null) {
258 throw new SAXException(
259 "Could not find changeSet to use for rollback: "
260 + path + ":" + author + ":" + id);
261 } else {
262 for (Change change : changeSet.getChanges()) {
263 this.changeSet.addRollbackChange(change);
264 }
265 }
266 }
267 inRollback = true;
268 } else if ("preConditions".equals(qName)) {
269 rootPrecondition = new PreconditionContainer();
270 rootPrecondition.setOnFail(StringUtils.trimToNull(atts
271 .getValue("onFail")));
272 rootPrecondition.setOnError(StringUtils.trimToNull(atts
273 .getValue("onError")));
274 rootPrecondition.setOnFailMessage(StringUtils.trimToNull(atts
275 .getValue("onFailMessage")));
276 rootPrecondition.setOnErrorMessage(StringUtils.trimToNull(atts
277 .getValue("onErrorMessage")));
278 rootPrecondition.setOnSqlOutput(StringUtils.trimToNull(atts
279 .getValue("onSqlOutput")));
280 preconditionLogicStack.push(rootPrecondition);
281 } else if (currentPrecondition != null && currentPrecondition instanceof CustomPreconditionWrapper && qName.equals("param")) {
282 ((CustomPreconditionWrapper) currentPrecondition).setParam(atts.getValue("name"), atts.getValue("value"));
283 } else if (rootPrecondition != null) {
284 currentPrecondition = PreconditionFactory.getInstance().create(
285 qName);
286
287 for (int i = 0; i < atts.getLength(); i++) {
288 String attributeName = atts.getQName(i);
289 String attributeValue = atts.getValue(i);
290 setProperty(currentPrecondition, attributeName,
291 attributeValue);
292 }
293 preconditionLogicStack.peek().addNestedPrecondition(
294 currentPrecondition);
295
296 if (currentPrecondition instanceof PreconditionLogic) {
297 preconditionLogicStack
298 .push(((PreconditionLogic) currentPrecondition));
299 }
300
301 if ("sqlCheck".equals(qName)) {
302 text = new StringBuffer();
303 }
304 } else if ("modifySql".equals(qName)) {
305 inModifySql = true;
306 if (StringUtils.trimToNull(atts.getValue("dbms")) != null) {
307 modifySqlDbmsList = new HashSet<String>(StringUtils
308 .splitAndTrim(atts.getValue("dbms"), ","));
309 }
310 if (StringUtils.trimToNull(atts.getValue("context")) != null) {
311 modifySqlContexts = new HashSet<String>(StringUtils
312 .splitAndTrim(atts.getValue("context"), ","));
313 }
314 if (StringUtils.trimToNull(atts.getValue("applyToRollback")) != null) {
315 modifySqlAppliedOnRollback = Boolean.valueOf(atts
316 .getValue("applyToRollback"));
317 }
318 } else if (inModifySql) {
319 SqlVisitor sqlVisitor = SqlVisitorFactory.getInstance().create(
320 qName);
321 for (int i = 0; i < atts.getLength(); i++) {
322 String attributeName = atts.getQName(i);
323 String attributeValue = atts.getValue(i);
324 setProperty(sqlVisitor, attributeName, attributeValue);
325 }
326 sqlVisitor.setApplicableDbms(modifySqlDbmsList);
327 sqlVisitor.setApplyToRollback(modifySqlAppliedOnRollback);
328 sqlVisitor.setContexts(modifySqlContexts);
329
330 changeSet.addSqlVisitor(sqlVisitor);
331 } else if (changeSet != null && change == null) {
332 change = ChangeFactory.getInstance().create(localName);
333 if (change == null) {
334 throw new SAXException("Unknown Liquibase extension: "
335 + localName
336 + ". Are you missing a jar from your classpath?");
337 }
338 change.setChangeSet(changeSet);
339 text = new StringBuffer();
340 if (change == null) {
341 throw new MigrationFailedException(changeSet,
342 "Unknown change: " + localName);
343 }
344 change.setResourceAccessor(resourceAccessor);
345 if (change instanceof CustomChangeWrapper) {
346 ((CustomChangeWrapper) change)
347 .setClassLoader(resourceAccessor.toClassLoader());
348 }
349 for (int i = 0; i < atts.getLength(); i++) {
350 String attributeName = atts.getLocalName(i);
351 String attributeValue = atts.getValue(i);
352 setProperty(change, attributeName, attributeValue);
353 }
354 change.init();
355 } else if (change != null && "column".equals(qName)) {
356 ColumnConfig column;
357 if (change instanceof LoadDataChange) {
358 column = new LoadDataColumnConfig();
359 } else {
360 column = new ColumnConfig();
361 }
362 for (int i = 0; i < atts.getLength(); i++) {
363 String attributeName = atts.getQName(i);
364 String attributeValue = atts.getValue(i);
365 setProperty(column, attributeName, attributeValue);
366 }
367 if (change instanceof ChangeWithColumns) {
368 ((ChangeWithColumns) change).addColumn(column);
369 } else {
370 throw new RuntimeException("Unexpected column tag for "
371 + change.getClass().getName());
372 }
373 } else if (change != null && "constraints".equals(qName)) {
374 ConstraintsConfig constraints = new ConstraintsConfig();
375 for (int i = 0; i < atts.getLength(); i++) {
376 String attributeName = atts.getQName(i);
377 String attributeValue = atts.getValue(i);
378 setProperty(constraints, attributeName, attributeValue);
379 }
380 ColumnConfig lastColumn = null;
381 if (change instanceof ChangeWithColumns) {
382 List<ColumnConfig> columns = ((ChangeWithColumns) change).getColumns();
383 if (columns != null && columns.size() > 0) {
384 lastColumn = columns.get(columns.size() - 1);
385 }
386 } else {
387 throw new RuntimeException("Unexpected change: "
388 + change.getClass().getName());
389 }
390 if (lastColumn == null) {
391 throw new RuntimeException("Could not determine column to add constraint to");
392 }
393 lastColumn.setConstraints(constraints);
394 } else if ("param".equals(qName)) {
395 if (change instanceof CustomChangeWrapper) {
396 if (atts.getValue("value") == null) {
397 paramName = atts.getValue("name");
398 text = new StringBuffer();
399 } else {
400 ((CustomChangeWrapper) change).setParam(atts
401 .getValue("name"), atts.getValue("value"));
402 }
403 } else {
404 throw new MigrationFailedException(changeSet,
405 "'param' unexpected in " + qName);
406 }
407 } else if ("where".equals(qName)) {
408 text = new StringBuffer();
409 } else if ("property".equals(qName)) {
410 String context = StringUtils.trimToNull(atts.getValue("context"));
411 String dbms = StringUtils.trimToNull(atts.getValue("dbms"));
412 if (StringUtils.trimToNull(atts.getValue("file")) == null) {
413 this.changeLogParameters.set(atts.getValue("name"), atts.getValue("value"), context, dbms);
414 } else {
415 Properties props = new Properties();
416 InputStream propertiesStream = resourceAccessor.getResourceAsStream(atts.getValue("file"));
417 if (propertiesStream == null) {
418 log.info("Could not open properties file "
419 + atts.getValue("file"));
420 } else {
421 props.load(propertiesStream);
422
423 for (Map.Entry entry : props.entrySet()) {
424 this.changeLogParameters.set(entry.getKey().toString(), entry.getValue().toString(), context, dbms);
425 }
426 }
427 }
428 } else if (change instanceof ExecuteShellCommandChange
429 && "arg".equals(qName)) {
430 ((ExecuteShellCommandChange) change).addArg(atts
431 .getValue("value"));
432 } else if (change != null) {
433 String creatorMethod = "create"
434 + localName.substring(0, 1).toUpperCase()
435 + localName.substring(1);
436
437 Object objectToCreateFrom;
438 if (changeSubObjects.size() == 0) {
439 objectToCreateFrom = change;
440 } else {
441 objectToCreateFrom = changeSubObjects.peek();
442 }
443
444 Method method;
445 try {
446 method = objectToCreateFrom.getClass().getMethod(
447 creatorMethod);
448 } catch (NoSuchMethodException e) {
449 throw new MigrationFailedException(changeSet,
450 "Could not find creator method " + creatorMethod
451 + " for tag: " + qName);
452 }
453 Object subObject = method.invoke(objectToCreateFrom);
454 for (int i = 0; i < atts.getLength(); i++) {
455 String attributeName = atts.getQName(i);
456 String attributeValue = atts.getValue(i);
457 setProperty(subObject, attributeName, attributeValue);
458 }
459 changeSubObjects.push(subObject);
460
461 } else {
462 throw new MigrationFailedException(changeSet,
463 "Unexpected tag: " + qName);
464 }
465 } catch (Exception e) {
466 log.severe("Error thrown as a SAXException: " + e.getMessage(), e);
467 e.printStackTrace();
468 throw new SAXException(e);
469 }
470 }
471
472 protected boolean handleIncludedChangeLog(String fileName,
473 boolean isRelativePath, String relativeBaseFileName)
474 throws LiquibaseException {
475 if (!(fileName.endsWith(".xml") || fileName.endsWith(".sql"))) {
476 log.debug(relativeBaseFileName + "/" + fileName
477 + " is not a recognized file type");
478 return false;
479 }
480
481 if (fileName.equalsIgnoreCase(".svn") || fileName.equalsIgnoreCase("cvs")) {
482 return false;
483 }
484
485 if (isRelativePath) {
486
487 String tempFile = FilenameUtils.concat(FilenameUtils.getFullPath(relativeBaseFileName), fileName);
488 if(tempFile != null && new File(tempFile).exists() == true) {
489 fileName = tempFile;
490 } else {
491 fileName = FilenameUtils.getFullPath(relativeBaseFileName) + fileName;
492 }
493 }
494 DatabaseChangeLog changeLog = ChangeLogParserFactory.getInstance().getParser(fileName, resourceAccessor).parse(fileName, changeLogParameters,
495 resourceAccessor);
496 PreconditionContainer preconditions = changeLog.getPreconditions();
497 if (preconditions != null) {
498 if (null == databaseChangeLog.getPreconditions()) {
499 databaseChangeLog.setPreconditions(new PreconditionContainer());
500 }
501 databaseChangeLog.getPreconditions().addNestedPrecondition(
502 preconditions);
503 }
504 for (ChangeSet changeSet : changeLog.getChangeSets()) {
505 handleChangeSet(changeSet);
506 }
507
508 return true;
509 }
510
511 private void setProperty(Object object, String attributeName,
512 String attributeValue) throws IllegalAccessException,
513 InvocationTargetException, CustomChangeException {
514 if (object instanceof CustomChangeWrapper) {
515 if (attributeName.equals("class")) {
516 ((CustomChangeWrapper) object).setClass(changeLogParameters
517 .expandExpressions(attributeValue));
518 } else {
519 ((CustomChangeWrapper) object).setParam(attributeName,
520 changeLogParameters.expandExpressions(attributeValue));
521 }
522 } else {
523 ObjectUtil.setProperty(object, attributeName, changeLogParameters
524 .expandExpressions(attributeValue));
525 }
526 }
527
528 @Override
529 public void endElement(String uri, String localName, String qName)
530 throws SAXException {
531 String textString = null;
532 if (text != null && text.length() > 0) {
533 textString = changeLogParameters.expandExpressions(StringUtils
534 .trimToNull(text.toString()));
535 }
536
537 try {
538 if (changeSubObjects.size() > 0) {
539 changeSubObjects.pop();
540 } else if (rootPrecondition != null) {
541 if ("preConditions".equals(qName)) {
542 if (changeSet == null) {
543 databaseChangeLog.setPreconditions(rootPrecondition);
544 handlePreCondition(rootPrecondition);
545 } else {
546 changeSet.setPreconditions(rootPrecondition);
547 }
548 rootPrecondition = null;
549 } else if ("and".equals(qName)) {
550 preconditionLogicStack.pop();
551 currentPrecondition = null;
552 } else if ("or".equals(qName)) {
553 preconditionLogicStack.pop();
554 currentPrecondition = null;
555 } else if ("not".equals(qName)) {
556 preconditionLogicStack.pop();
557 currentPrecondition = null;
558 } else if (qName.equals("sqlCheck")) {
559 ((SqlPrecondition) currentPrecondition).setSql(textString);
560 currentPrecondition = null;
561 } else if (qName.equals("customPrecondition")) {
562 ((CustomPreconditionWrapper) currentPrecondition).setClassLoader(resourceAccessor.toClassLoader());
563 currentPrecondition = null;
564 }
565
566 } else if (changeSet != null && "rollback".equals(qName)) {
567 changeSet.addRollBackSQL(textString);
568 inRollback = false;
569 } else if (change != null && change instanceof RawSQLChange
570 && "comment".equals(qName)) {
571 ((RawSQLChange) change).setComments(textString);
572 text = new StringBuffer();
573 } else if (change != null && "where".equals(qName)) {
574 if (change instanceof UpdateDataChange) {
575 ((UpdateDataChange) change).setWhereClause(textString);
576 } else if (change instanceof DeleteDataChange) {
577 ((DeleteDataChange) change).setWhereClause(textString);
578 } else {
579 throw new RuntimeException("Unexpected change type: "
580 + change.getClass().getName());
581 }
582 text = new StringBuffer();
583 } else if (change != null
584 && change instanceof CreateProcedureChange
585 && "comment".equals(qName)) {
586 ((CreateProcedureChange) change).setComments(textString);
587 text = new StringBuffer();
588 } else if (change != null && change instanceof CustomChangeWrapper
589 && paramName != null && "param".equals(qName)) {
590 ((CustomChangeWrapper) change).setParam(paramName, textString);
591 text = new StringBuffer();
592 paramName = null;
593 } else if (changeSet != null && "comment".equals(qName)) {
594 changeSet.setComments(textString);
595 text = new StringBuffer();
596 } else if (changeSet != null && "changeSet".equals(qName)) {
597 handleChangeSet(changeSet);
598 changeSet = null;
599 } else if (change != null && qName.equals("column")
600 && textString != null) {
601 if (change instanceof InsertDataChange) {
602 List<ColumnConfig> columns = ((InsertDataChange) change)
603 .getColumns();
604 columns.get(columns.size() - 1).setValue(textString);
605 } else if (change instanceof UpdateDataChange) {
606 List<ColumnConfig> columns = ((UpdateDataChange) change)
607 .getColumns();
608 columns.get(columns.size() - 1).setValue(textString);
609 } else {
610 throw new RuntimeException("Unexpected column with text: "
611 + textString);
612 }
613 this.text = new StringBuffer();
614 } else if (change != null
615 && localName.equals(change.getChangeMetaData().getName())) {
616 if (textString != null) {
617 if (change instanceof RawSQLChange) {
618 ((RawSQLChange) change).setSql(textString);
619 } else if (change instanceof CreateProcedureChange) {
620 ((CreateProcedureChange) change)
621 .setProcedureBody(textString);
622
623
624
625 } else if (change instanceof CreateViewChange) {
626 ((CreateViewChange) change).setSelectQuery(textString);
627 } else if (change instanceof StopChange) {
628 ((StopChange) change).setMessage(textString);
629 } else {
630 throw new RuntimeException("Unexpected text in "
631 + change.getChangeMetaData().getName());
632 }
633 }
634 text = null;
635 if (inRollback) {
636 changeSet.addRollbackChange(change);
637 } else {
638 changeSet.addChange(change);
639 }
640 change = null;
641 } else if (changeSet != null && "validCheckSum".equals(qName)) {
642 changeSet.addValidCheckSum(text.toString());
643 text = null;
644 } else if ("modifySql".equals(qName)) {
645 inModifySql = false;
646 modifySqlDbmsList = null;
647 modifySqlContexts = null;
648 modifySqlAppliedOnRollback = false;
649 }
650 } catch (Exception e) {
651 log.severe("Error thrown as a SAXException: " + e.getMessage(), e);
652 throw new SAXException(databaseChangeLog.getPhysicalFilePath()
653 + ": " + e.getMessage(), e);
654 }
655 }
656
657 protected void handlePreCondition(
658 @SuppressWarnings("unused") Precondition precondition) {
659 databaseChangeLog.setPreconditions(rootPrecondition);
660 }
661
662 protected void handleChangeSet(ChangeSet changeSet) {
663 databaseChangeLog.addChangeSet(changeSet);
664 }
665
666 @Override
667 public void characters(char ch[], int start, int length)
668 throws SAXException {
669 if (text != null) {
670 text.append(new String(ch, start, length));
671 }
672 }
673
674
675
676
677 private class ExpandingAttributes implements Attributes {
678 private Attributes attributes;
679
680 private ExpandingAttributes(Attributes attributes) {
681 this.attributes = attributes;
682 }
683
684 public int getLength() {
685 return attributes.getLength();
686 }
687
688 public String getURI(int index) {
689 return attributes.getURI(index);
690 }
691
692 public String getLocalName(int index) {
693 return attributes.getLocalName(index);
694 }
695
696 public String getQName(int index) {
697 return attributes.getQName(index);
698 }
699
700 public String getType(int index) {
701 return attributes.getType(index);
702 }
703
704 public String getValue(int index) {
705 return attributes.getValue(index);
706 }
707
708 public int getIndex(String uri, String localName) {
709 return attributes.getIndex(uri, localName);
710 }
711
712 public int getIndex(String qName) {
713 return attributes.getIndex(qName);
714 }
715
716 public String getType(String uri, String localName) {
717 return attributes.getType(uri, localName);
718 }
719
720 public String getType(String qName) {
721 return attributes.getType(qName);
722 }
723
724 public String getValue(String uri, String localName) {
725 return changeLogParameters.expandExpressions(attributes.getValue(
726 uri, localName));
727 }
728
729 public String getValue(String qName) {
730 return changeLogParameters.expandExpressions(attributes
731 .getValue(qName));
732 }
733 }
734
735 static File extractZipFile(URL resource) throws IOException {
736 String file = resource.getFile();
737 String path = file.split("!")[0];
738 if(path.matches("file:\\/[A-Za-z]:\\/.*")) {
739 path = path.replaceFirst("file:\\/", "");
740 }else {
741 path = path.replaceFirst("file:", "");
742 }
743 path = URLDecoder.decode(path);
744 File zipfile = new File(path);
745
746 File tempDir = File.createTempFile("liquibase-sax", ".dir");
747 tempDir.delete();
748 tempDir.mkdir();
749
750
751 JarFile jarFile = new JarFile(zipfile);
752 Enumeration<JarEntry> entries = jarFile.entries();
753 while (entries.hasMoreElements()) {
754 JarEntry entry = entries.nextElement();
755 File entryFile = new File(tempDir, entry.getName());
756 entryFile.mkdirs();
757 }
758
759 return tempDir;
760 }
761 }