//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.samskivert.jdbc.depot;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;

import static com.samskivert.jdbc.depot.Log.log;

/**
 * These can be registered with the {@link PersistenceContext} to effect hand-coded migrations
 * between entity versions. The modifier should override {@link #invoke} to perform its
 * migrations. See {@link PersistenceContext#registerPreMigration} and {@link
 * PersistenceContext#registerPostMigration}.
 */
public abstract class EntityMigration extends Modifier
{
    /**
     * A convenient migration for dropping a column from an entity.
     */
    public static class Drop extends EntityMigration
    {
        public Drop (int targetVersion, String columnName) {
            super(targetVersion);
            _columnName = columnName;
        }

        public int invoke (Connection conn) throws SQLException {
            Statement stmt = conn.createStatement();
            try {
                log.info("Dropping '" + _columnName + "' from " + _tableName);
                return stmt.executeUpdate(
                    "alter table " + _tableName + " drop column " + _columnName);
            } finally {
                stmt.close();
            }
        }

        protected String _columnName;
    }

    /**
     * A convenient migration for renaming a column in an entity.
     */
    public static class Rename extends EntityMigration
    {
        public Rename (int targetVersion, String oldColumnName, String newColumnName) {
            super(targetVersion);
            _oldColumnName = oldColumnName;
            _newColumnName = newColumnName;
        }

        public int invoke (Connection conn) throws SQLException {
            Statement stmt = conn.createStatement();
            try {
                log.info("Changing '" + _oldColumnName + "' to '" + _newColumnDef + "' in " +
                         _tableName);
                return stmt.executeUpdate(
                    "alter table " + _tableName + " change column " + _oldColumnName + " " +
                    _newColumnDef);
            } finally {
                stmt.close();
            }
        }

        protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers)
        {
            super.init(tableName, marshallers);
            _newColumnDef = marshallers.get(_newColumnName).getColumnDefinition();
        }

        protected String _oldColumnName,  _newColumnName, _newColumnDef;
    }

    /**
     * If this method returns true, this migration will be run <b>before</b> the default
     * migrations, if false it will be run after.
     */
    public boolean runBeforeDefault ()
    {
        return true;
    }

    /**
     * When an Entity is being migrated, this method will be called to check whether this migration
     * should be run. The default implementation runs as long as the currentVersion is less than
     * the target version supplied to the migration at construct time.
     */
    public boolean shouldRunMigration (int currentVersion, int targetVersion)
    {
        return (currentVersion < _targetVersion);
    }

    protected EntityMigration (int targetVersion)
    {
        super();
        _targetVersion = targetVersion;
    }

    /**
     * This is called to provide the migration with the name of the entity table and access to its
     * field marshallers prior to being invoked. This will <em>only</em> be called after this
     * migration has been determined to be runnable so one cannot rely on this method having been
     * called in {@link #shouldRunMigration}.
     */
    protected void init (String tableName, HashMap<String,FieldMarshaller> marshallers)
    {
        _tableName = tableName;
    }

    protected int _targetVersion;
    protected String _tableName;
}

@@ -107,10 +107,9 @@
 e%0A//
- 
 %0A
+
 // T
@@ -987,16 +987,54 @@
 shMap;%0A%0A
+import com.samskivert.jdbc.JDBCUtil;%0A%0A
 import s
@@ -2824,32 +2824,33 @@
    _tableName);%0A
+%0A
                 
@@ -2853,109 +2853,804 @@
     
-return stmt.executeUpdate(%0A                    %22alter table %22 + _tableName + %22 change column 
+if (!JDBCUtil.tableContainsColumn(conn, _tableName, _oldColumnName)) %7B%0A                    if (JDBCUtil.tableContainsColumn(conn, _tableName, _newColumnName)) %7B%0A                        // we'll accept this inconsistency%0A                        log.warning(%22Column rename appears already performed.%22);%0A                        return 0;%0A                    %7D%0A                    // but this is not OK%0A                    throw new IllegalArgumentException(%0A                        _tableName + %22 does not contain '%22 + _oldColumnName + %22'%22);%0A                %7D%0A%0A                // nor is this%0A                if (JDBCUtil.tableContainsColumn(conn, _tableName, _newColumnName)) %7B%0A                    throw new IllegalArgumentException(%0A                        _tableName + %22 already contains '
 %22 + _
-old
+new
 Colu
@@ -3663,32 +3663,188 @@
  + %22
- %22 +%0A                   
+'%22);%0A                %7D%0A%0A                return stmt.executeUpdate(%22alter table %22 + _tableName + %22 change column %22 +%0A                                          _oldColumnName + %22 %22 +
  _ne
@@ -3856,16 +3856,17 @@
 mnDef);%0A
+%0A
         

Make our Rename migration more lenient about having already been performed.