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