001// Copyright 2011 Leo Przybylski. All rights reserved.
002//
003// Redistribution and use in source and binary forms, with or without modification, are
004// permitted provided that the following conditions are met:
005//
006//    1. Redistributions of source code must retain the above copyright notice, this list of
007//       conditions and the following disclaimer.
008//
009//    2. Redistributions in binary form must reproduce the above copyright notice, this list
010//       of conditions and the following disclaimer in the documentation and/or other materials
011//       provided with the distribution.
012//
013// THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED
014// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
015// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
016// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
017// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
018// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
019// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
020// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
021// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
022//
023// The views and conclusions contained in the software and documentation are those of the
024// authors and should not be interpreted as representing official policies, either expressed
025// or implied, of Leo Przybylski.
026package org.kualigan.tools.liquibase;
027
028import java.io.IOException;
029import java.io.InputStream;
030
031import javax.xml.parsers.SAXParser;
032import javax.xml.parsers.SAXParserFactory;
033
034import liquibase.changelog.ChangeLogParameters;
035import liquibase.changelog.DatabaseChangeLog;
036import liquibase.exception.ChangeLogParseException;
037import liquibase.logging.LogFactory;
038import liquibase.parser.ChangeLogParser;
039import liquibase.parser.core.xml.*;
040import liquibase.resource.ResourceAccessor;
041import liquibase.util.file.FilenameUtils;
042
043import org.xml.sax.ErrorHandler;
044import org.xml.sax.InputSource;
045import org.xml.sax.SAXException;
046import org.xml.sax.SAXNotRecognizedException;
047import org.xml.sax.SAXNotSupportedException;
048import org.xml.sax.SAXParseException;
049import org.xml.sax.XMLReader;
050
051public class XMLChangeLogParser extends liquibase.parser.core.xml.XMLChangeLogSAXParser {
052
053    public static String getSchemaVersion() {
054        return "2.0";
055    }
056
057    public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParameters changeLogParameters, ResourceAccessor resourceAccessor) throws ChangeLogParseException {
058
059        InputStream inputStream = null;
060        try {
061            
062            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
063            try {
064                parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
065            } catch (SAXNotRecognizedException e) {
066                //ok, parser must not support it
067            } catch (SAXNotSupportedException e) {
068                //ok, parser must not support it
069            }
070
071            XMLReader xmlReader = parser.getXMLReader();
072            LiquibaseEntityResolver resolver=new LiquibaseEntityResolver();
073            resolver.useResoureAccessor(resourceAccessor,FilenameUtils.getFullPath(physicalChangeLogLocation));
074            xmlReader.setEntityResolver(resolver);
075            xmlReader.setErrorHandler(new ErrorHandler() {
076                public void warning(SAXParseException exception) throws SAXException {
077                    LogFactory.getLogger().warning(exception.getMessage());
078                    throw exception;
079                }
080
081                public void error(SAXParseException exception) throws SAXException {
082                    LogFactory.getLogger().severe(exception.getMessage());
083                    throw exception;
084                }
085
086                public void fatalError(SAXParseException exception) throws SAXException {
087                    LogFactory.getLogger().severe(exception.getMessage());
088                    throw exception;
089                }
090            });
091                
092            inputStream = resourceAccessor.getResourceAsStream(physicalChangeLogLocation);
093            if (inputStream == null) {
094                throw new ChangeLogParseException(physicalChangeLogLocation + " does not exist");
095            }
096
097            XMLChangeLogSAXHandler contentHandler = new XMLChangeLogSAXHandler(physicalChangeLogLocation, resourceAccessor, changeLogParameters);
098            xmlReader.setContentHandler(contentHandler);
099            xmlReader.parse(new InputSource(inputStream));
100
101            return contentHandler.getDatabaseChangeLog();
102        } catch (ChangeLogParseException e) {
103            throw e;
104        } catch (IOException e) {
105            throw new ChangeLogParseException("Error Reading Migration File: " + e.getMessage(), e);
106        } catch (SAXParseException e) {
107            throw new ChangeLogParseException("Error parsing line " + e.getLineNumber() + " column " + e.getColumnNumber() + " of " + physicalChangeLogLocation +": " + e.getMessage(), e);
108        } catch (SAXException e) {
109            Throwable parentCause = e.getException();
110            while (parentCause != null) {
111                if (parentCause instanceof ChangeLogParseException) {
112                    throw ((ChangeLogParseException) parentCause);
113                }
114                parentCause = parentCause.getCause();
115            }
116            String reason = e.getMessage();
117            String causeReason = null;
118            if (e.getCause() != null) {
119                causeReason = e.getCause().getMessage();
120            }
121
122//            if (reason == null && causeReason==null) {
123//                reason = "Unknown Reason";
124//            }
125            if (reason == null) {
126                if (causeReason != null) {
127                    reason = causeReason;
128                } else {
129                    reason = "Unknown Reason";
130                }
131            }
132
133            throw new ChangeLogParseException("Invalid Migration File: " + reason, e);
134        } catch (Exception e) {
135            throw new ChangeLogParseException(e);
136        } finally {
137            if (inputStream != null) {
138                try {
139                    inputStream.close();
140                } catch (IOException e) {
141                    // probably ok
142                }
143            }
144        }
145    }
146}