All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.parser.ext.GroovyLiquibaseChangeLogParser.groovy Maven / Gradle / Ivy

/*
 * Copyright 2011-2015 Tim Berglund and Steven C. Saliman
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package liquibase.parser.ext

import liquibase.parser.ChangeLogParser
import liquibase.changelog.DatabaseChangeLog
import liquibase.changelog.ChangeLogParameters
import liquibase.resource.ResourceAccessor
import liquibase.exception.ChangeLogParseException

import org.liquibase.groovy.delegate.DatabaseChangeLogDelegate
import org.codehaus.groovy.control.CompilerConfiguration

/**
 * This is the main parser class for the Liquibase Groovy DSL.  It is the
 * integration point to Liquibase itself.  It must be in the
 * liquibase.parser.ext package to be found by Liquibase at runtime.
 *
 * @author Tim Berglund
 * @author Steven C. Saliman
 */
class GroovyLiquibaseChangeLogParser
  implements ChangeLogParser {


  DatabaseChangeLog parse(String physicalChangeLogLocation,
                          ChangeLogParameters changeLogParameters,
                          ResourceAccessor resourceAccessor) {

    physicalChangeLogLocation = physicalChangeLogLocation.replaceAll('\\\\', '/')
    physicalChangeLogLocation = physicalChangeLogLocation.replace(System.getProperty("user.dir").toString() + "/", "")
    def inputStreams = resourceAccessor.getResourcesAsStream(physicalChangeLogLocation)
    if ( !inputStreams || inputStreams.size() < 1 ) {
      throw new ChangeLogParseException(physicalChangeLogLocation + " does not exist")
    }
	  def inputStream = inputStreams.toArray()[0]

    try {
      def changeLog = new DatabaseChangeLog(physicalChangeLogLocation)
      changeLog.setChangeLogParameters(changeLogParameters)

      def binding = new Binding()
      def shell = new GroovyShell(binding)

      // Parse the script, give it the local changeLog instance, give it access
      // to root-level method delegates, and call.
      def script = shell.parse(new InputStreamReader(inputStream, "UTF8"))
      script.metaClass.getDatabaseChangeLog = { -> changeLog }
      script.metaClass.getResourceAccessor = { -> resourceAccessor }
      script.metaClass.methodMissing = changeLogMethodMissing
      script.run()

      // The changeLog will have been populated by the script
      return changeLog
    }
    finally {
      try {
        inputStream.close()
      }
      catch(Exception e) {
        // Can't do much more than hope for the best here
      }
    }
  }


  boolean supports(String changeLogFile, ResourceAccessor resourceAccessor) {
    changeLogFile.endsWith('.groovy')
  }


  int getPriority() {
    PRIORITY_DEFAULT
  }


  def getChangeLogMethodMissing() {
    { name, args ->
      if(name == 'databaseChangeLog') {
        processDatabaseChangeLogRootElement(databaseChangeLog, resourceAccessor, args)
      }
      else {
        throw new ChangeLogParseException("Unrecognized root element ${name}")
      }
    }
  }

  private def processDatabaseChangeLogRootElement(databaseChangeLog, resourceAccessor, args) {
    def delegate;
    def closure;

    switch(args.size()) {
      case 0:
        throw new ChangeLogParseException("databaseChangeLog element cannot be empty")

      case 1:
        closure = args[0]
        if(!(closure instanceof Closure)) {
          throw new ChangeLogParseException("databaseChangeLog element must be followed by a closure (databaseChangeLog { ... })")
        }
        delegate = new DatabaseChangeLogDelegate(databaseChangeLog)
        break

      case 2:
        def params = args[0]
        closure = args[1]
        if(!(params instanceof Map)) {
          throw new ChangeLogParseException("databaseChangeLog element must take parameters followed by a closure (databaseChangeLog(key: value) { ... })")
        }
        if(!(closure instanceof Closure)) {
          throw new ChangeLogParseException("databaseChangeLog element must take parameters followed by a closure (databaseChangeLog(key: value) { ... })")
        }
        delegate = new DatabaseChangeLogDelegate(params, databaseChangeLog)
        break

      default:
        throw new ChangeLogParseException("databaseChangeLog element has too many parameters: ${args}")
    }

    delegate.resourceAccessor = resourceAccessor
    closure.delegate = delegate
    closure.resolveStrategy = Closure.OWNER_FIRST
    closure.call()
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy