/**
 * ADempiere contribution
 * Author: Karsten Thiemann, kthiemann@adempiere.org
 * Compiere/Adempiere migration script generation.
 */

package oracle;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;

/**
 * Class to compare two oracle compiere/adempiere databases. It creates a sql
 * script to upgrade the first db to the second. Please read the readme.txt
 * before you use it!
 * 
 * @author Karsten Thiemann, kt@schaeffer-ag.de
 * 
 */
public class DBDifference {

	// global setting - please adjust them to your needs...

	/** name of first database - the one you want to migrate (old compiere) */
	private static final String DB1_NAME = "c253a";

	/** url of first database - check servername and port */
	private static final String DB1_URL = "jdbc:oracle:thin:@meitner:1521:";

	/** user name of first database - should be compiere */
	private static final String DB1_USER = "compiere";

	/** password first database */
	private static final String DB1_PASSWD = "compiere";

	/** name of second database - the migration target (adempiere) */
	private static final String DB2_NAME = "c253b";

	/** user name of second database - should be adempiere or compiere */
	private static final String DB2_USER = "compiere";

	/** password second database */
	private static final String DB2_PASSWD = "compiere";

	/** url of second database - check servername and port */
	private static final String DB2_URL = "jdbc:oracle:thin:@meitner:1521:";

	/**
	 * id of the (custom)clients admin role. If not null access to new
	 * forms/windows is added
	 */
	private static final String AD_ROLE_ID = "1000000";

	/**
	 * id of the (custom) client. If not null access to new forms/windows is
	 * added
	 */
	private static final String AD_CLIENT_ID = "1000000";

	/** time format used for AD_* entries */
	private static final String TIME_FORMAT = "RRRR-MM-DD";

	// global variables - no need to edit them...
	/** new tables to create */
	private Vector<Table> m_newTables = new Vector<Table>();

	/** tables with additional columns */
	private Vector<Table> m_changedTables = new Vector<Table>();

	/** tables missing in DB2 */
	private Vector<Table> m_tablesToDrop = new Vector<Table>();

	/** new views to create */
	private Vector<View> m_newViews = new Vector<View>();

	/** changed views */
	private Vector<View> m_changedViews = new Vector<View>();

	/** new constraints */
	private Vector<Constraint> m_newConstraints = new Vector<Constraint>();

	/** constraints missing in db2 if not a customization you can drop them */
	private Vector<Constraint> m_constraintsToDrop = new Vector<Constraint>();

	/** new lines for AD_Tables */
	private Vector<String> m_newTableEntry = new Vector<String>();

	/** changed lines for AD_Tables */
	private Vector<String> m_alterADEntry = new Vector<String>();
	
	/** deleted lines for AD_Tables */
	private Vector<String> m_deleteADEntry = new Vector<String>();

	/** new functions/procedure statements to create */
	private Vector<String> m_newFunctionStatements = new Vector<String>();

	/** new index statements to create */
	private Vector<String> m_newIndexStatements = new Vector<String>();

	/** drop index statements */
	private Vector<String> m_dropIndexStatements = new Vector<String>();

	/** functions/procedure statements to drop */
	private Vector<String> m_dropFunctionStatements = new Vector<String>();

	/** triggers to drop */
	private Vector<String> m_dropTriggerStatements = new Vector<String>();

	/** unappliable statements */
	private Vector<String> m_unappliableStatements = new Vector<String>();

	private Statement stmtdb1 = null;

	private Statement stmtdb2 = null;

	private Connection con1 = null;

	private Connection con2 = null;

	/**
	 * Main method...
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		new DBDifference().showDifference();
	}

	/**
	 * Compares the two databases and prints out the sql statements and hints to
	 * update the first db to the second.
	 * 
	 * @param db1
	 *            name of first database (working db)
	 * @param db2
	 *            name of second database (reference db)
	 */
	public void showDifference() {
		try {
			con1 = getConnection(DB1_NAME, DB1_USER, DB1_PASSWD, DB1_URL);
			con1.setAutoCommit(false);
			con2 = getConnection(DB2_NAME, DB2_USER, DB2_PASSWD, DB2_URL);
			con2.setAutoCommit(false);

			stmtdb1 = con1.createStatement();
			stmtdb2 = con2.createStatement();

			 System.out.println("compare tables ...");
			 compareTables();
			 System.out.println("compare constraints ...");
			 compareConstraints();
			
			 System.out.println("compare views ...");
			 compareViews();
			
			 System.out.println("compare functions/procedures ...");
			 compareFunctionsAndProcedures();
			
			 System.out.println("drop triggers ...");
			 dropTriggers();
			
			 System.out.println("compare ad_elements ...");
			 compareADElements();

			System.out.println("compare indexes ...");
			compareIndexes();

			sortAndPrintSQL();

			stmtdb1.close();
			stmtdb2.close();

			con1.close();
			con2.close();

			System.out.println("done.");

		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Newer compiere dbs don't use triggers. So just create drop statements.
	 * 
	 * @throws SQLException
	 */
	private void dropTriggers() throws SQLException {
		String sql = "select trigger_name from user_triggers order by trigger_name";
		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			m_dropTriggerStatements.add("DROP " + rs.getString("TRIGGER_NAME") + ";");
		}
		rs.close();

	}

	/**
	 * Compare indexes.
	 * 
	 * @throws SQLException
	 */
	private void compareIndexes() throws SQLException {
		final Vector<String> indexNamesDB1 = new Vector<String>();
		final Vector<String> indexNamesDB2 = new Vector<String>();
		String sql = "select index_name, uniqueness, table_name from user_indexes "
				+ " where index_type='NORMAL' and index_name not like 'SYS_%' and index_name not like 'BIN$%'";
		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			indexNamesDB1.add(rs.getString("INDEX_NAME"));
		}
		rs.close();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			indexNamesDB2.add(rs.getString("INDEX_NAME"));
		}
		rs.close();

		final Vector<String> newIndexes = getNewElements(indexNamesDB1, indexNamesDB2);
		for (int i = 0; i < newIndexes.size(); i++) {
			if (!newIndexes.get(i).endsWith("KEY")) {
				// key indexes are generated by oracle when you create the table
				createNewIndexEntry(newIndexes.get(i));
			}

		}
		// drop indexes not found in db2
		final Vector<String> missingIndexes = getMissingElements(indexNamesDB1, indexNamesDB2);
		for (int i = 0; i < missingIndexes.size(); i++) {
			if (!missingIndexes.get(i).endsWith("KEY")) {
				//unable to drop key indexes - they are dropped if table is dropped
				m_dropIndexStatements.add("DROP INDEX " + missingIndexes.get(i) + ";");
			}
		}
		// find changed index
		for (int i = 0; i < indexNamesDB2.size(); i++) {
			if (indexNamesDB1.contains(indexNamesDB2.get(i))) {
				// index found in both dbs, test for changes
				// get columns for index
				final Vector<String> columnNames1 = new Vector<String>();
				final Vector<String> columnNames2 = new Vector<String>();
				sql = "select column_name from user_ind_columns where index_name='"
						+ indexNamesDB2.get(i) + "' order by column_position";
				rs = stmtdb1.executeQuery(sql);
				while (rs.next()) {
					columnNames1.add(rs.getString("COLUMN_NAME"));
				}
				rs.close();
				rs = stmtdb2.executeQuery(sql);
				while (rs.next()) {
					columnNames2.add(rs.getString("COLUMN_NAME"));
				}
				rs.close();
				// simple comparison - just compare the length...
				if (columnNames1.size() != columnNames2.size()) {
					if (!indexNamesDB2.get(i).endsWith("KEY")) {
						// cant drop key indexes...
						m_dropIndexStatements.add("DROP INDEX " + indexNamesDB2.get(i) + ";");
						createNewIndexEntry(indexNamesDB2.get(i));
					}
				}

			}
		}
	}

	/**
	 * Creates create index statements for the giving indexName and adds them to
	 * the global m_newIndexStatements Vector.
	 * 
	 * @param newIndexes
	 * @param i
	 * @throws SQLException
	 */
	private void createNewIndexEntry(String indexName) throws SQLException {
		ResultSet rs;
		String unique = "";
		String tableName = "";
		rs = stmtdb2
				.executeQuery("select table_name, uniqueness from user_indexes where index_name='"
						+ indexName + "'");
		if (rs.next()) {
			tableName = rs.getString("TABLE_NAME");
			unique = rs.getString("UNIQUENESS");
			if (unique.equals("NONUNIQUE")) {
				unique = "";
			}
		}
		rs.close();
		String createStatement = "CREATE " + unique + " INDEX " + indexName + " ON " + tableName
				+ " (";
		// get columns for index
		rs = stmtdb2.executeQuery("select column_name from user_ind_columns where index_name='"
				+ indexName + "' order by column_position");
		int k = 0;
		while (rs.next()) {
			if (k != 0) {
				createStatement += " ,";
			}
			createStatement += rs.getString("COLUMN_NAME");
			k++;
		}
		rs.close();
		createStatement += ");";
		m_newIndexStatements.add(createStatement);
	}

	/**
	 * Compares the functions and procedures of the two dbs.
	 * 
	 * @throws SQLException
	 */
	private void compareFunctionsAndProcedures() throws SQLException {
		final Vector<String> functionNamesDB1 = new Vector<String>();
		final Vector<String> functionNamesDB2 = new Vector<String>();
		// user_procedures holds functions and procedures...
		String sql = "select object_name from user_procedures order by object_name";
		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			functionNamesDB1.add(rs.getString("OBJECT_NAME"));
		}
		rs.close();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			functionNamesDB2.add(rs.getString("OBJECT_NAME"));
		}
		rs.close();
		// compare existing functions
		for (int i = 0; i < functionNamesDB2.size(); i++) {
			if (functionNamesDB1.contains(functionNamesDB2.get(i))) {
				sql = "select text from user_source where name='" + functionNamesDB2.get(i) + "'";
				rs = stmtdb1.executeQuery(sql);
				String text1 = "";
				String text2 = "";
				if (rs.next()) {
					text1 = rs.getString("TEXT");
				}
				rs.close();
				rs = stmtdb2.executeQuery(sql);
				if (rs.next()) {
					text2 = rs.getString("TEXT");
				}
				rs.close();
				if (!text2.equals(text1)) {
					// changed function
					System.out.println("FUNCTION OR PROCEDURE " + functionNamesDB2.get(i)
							+ " HAS CHANGED - please check it for customizations");
					rs = stmtdb2.executeQuery("select text from user_source where name='"
							+ functionNamesDB2.get(i) + "'");
					String createStatement = "create or replace ";
					while (rs.next()) {
						createStatement += rs.getString("TEXT");
					}
					m_newFunctionStatements.add(createStatement);
				}
			}
		}

		System.out.println("searching new functions/procedures ...");
		final Vector<String> newFunctions = getNewElements(functionNamesDB1, functionNamesDB2);
		for (int i = 0; i < newFunctions.size(); i++) {
			rs = stmtdb2.executeQuery("select text from user_source where name='"
					+ newFunctions.get(i) + "'");
			String createStatement = " CREATE OR REPLACE ";
			while (rs.next()) {
				createStatement += rs.getString("TEXT");
			}
			m_newFunctionStatements.add(createStatement);
		}
		final Vector<String> missingFunctions = getMissingElements(functionNamesDB1,
				functionNamesDB2);
		for (int i = 0; i < missingFunctions.size(); i++) {
			m_dropFunctionStatements.add(" DROP FUNCTION " + missingFunctions.get(i) + ";");
		}

	}

	/**
	 * Compares the views of the two dbs.
	 * 
	 * @throws SQLException
	 */
	private void compareViews() throws SQLException {
		final Vector<View> viewsDB1 = new Vector<View>();
		final Vector<View> viewsDB2 = new Vector<View>();
		String sql = "select view_name, text from user_views where view_name not like 'BIN$%'";
		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			viewsDB1.add(new View(rs.getString("VIEW_NAME"), rs.getString("TEXT")));
		}
		rs.close();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			viewsDB2.add(new View(rs.getString("VIEW_NAME"), rs.getString("TEXT")));
		}
		rs.close();
		// find new views
		for (int i = 0; i < viewsDB2.size(); i++) {
			String name = viewsDB2.get(i).getName();
			String text = viewsDB2.get(i).getText();
			boolean found = false;
			boolean identical = false;
			for (int j = 0; j < viewsDB1.size(); j++) {
				if (name.equals(viewsDB1.get(j).getName())) {
					found = true;
					if (text.equals(viewsDB1.get(j).getText())) {
						identical = true;
					}
					break;
				}
			}
			if (!found) {
				// add view to new views
				m_newViews.add(viewsDB2.get(i));
			} else if (!identical) {
				// add view to changed views
				m_changedViews.add(viewsDB2.get(i));
			}
		}
		// find views missing in db2 - no need to drop them
		for (int i = 0; i < viewsDB1.size(); i++) {
			String name = viewsDB1.get(i).getName();
			boolean found = false;
			for (int j = 0; j < viewsDB2.size(); j++) {
				if (name.equals(viewsDB2.get(j).getName())) {
					found = true;
					break;
				}
			}
			if (!found) {
				System.out.println();
				System.out.println("THE VIEW " + name
						+ " DOESN'T EXIST IN NEW DB - but no need to drop them...");
				System.out.println();
			}
		}
	}

	/**
	 * Compares the tables of the two dbs.
	 * 
	 * @throws SQLException
	 */
	private void compareTables() throws SQLException {
		final Vector<String> tableNamesDB1 = new Vector<String>();
		final Vector<String> tableNamesDB2 = new Vector<String>();
		String sql = "select table_name from user_tables where table_name not like 'BIN$%'";
		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			tableNamesDB1.add(rs.getString("TABLE_NAME"));
		}
		rs.close();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			tableNamesDB2.add(rs.getString("TABLE_NAME"));
		}
		rs.close();
		System.out.println("searching new tables ...");
		final Vector<String> newTables = getNewElements(tableNamesDB1, tableNamesDB2);
		for (int i = 0; i < newTables.size(); i++) {
			final String tableName = newTables.get(i);
			final Table table = new Table(tableName);
			sql = "select * from user_tab_columns where table_name='" + tableName + "'";
			rs = stmtdb2.executeQuery(sql);
			while (rs.next()) {
				Column column = new Column(rs);
				table.addColumn(column);
			}
			rs.close();
			m_newTables.add(table);
			//createEntriesForTable(tableName);
		}
		System.out.println("searching missing tables ...");
		final Vector<String> missingTables = getMissingElements(tableNamesDB1, tableNamesDB2);
		for (int i = 0; i < missingTables.size(); i++) {
			final Table table = new Table(missingTables.get(i));
			m_tablesToDrop.add(table);
		}
		System.out.println("searching changed tables ...");
		addChangedTablesToGlobalVector(tableNamesDB1, tableNamesDB2);
	}

	/**
	 * Searches for changed tables and adds them to the global m_changedTables
	 * Vector.
	 * 
	 * @param tableNamesDB1
	 * @param tableNamesDB2
	 * @throws SQLException
	 */
	private void addChangedTablesToGlobalVector(Vector<String> tableNamesDB1,
			Vector<String> tableNamesDB2) throws SQLException {
		final PreparedStatement stmtGetColumNamesForTable1 = con1
				.prepareStatement("select column_name from user_tab_columns where table_name=?");
		final PreparedStatement stmtGetColumNamesForTable2 = con2
				.prepareStatement("select column_name from user_tab_columns where table_name=?");
		final PreparedStatement stmtGetColumDetailsDB1 = con1
				.prepareStatement("select * from user_tab_columns where column_name=? and table_name=?");
		final PreparedStatement stmtGetColumDetailsDB2 = con2
				.prepareStatement("select * from user_tab_columns where column_name=? and table_name=?");

		Iterator iter = tableNamesDB2.iterator();
		while (iter.hasNext()) {
			String tableName = (String) iter.next();
			if (tableNamesDB1.contains(tableName)) {
				// get all column names for this table
				final Vector<String> columnNamesDB1 = new Vector<String>();
				final Vector<String> columnNamesDB2 = new Vector<String>();
				stmtGetColumNamesForTable1.setString(1, tableName);
				stmtGetColumNamesForTable2.setString(1, tableName);
				ResultSet rs = stmtGetColumNamesForTable1.executeQuery();
				while (rs.next()) {
					columnNamesDB1.add(rs.getString("COLUMN_NAME"));
				}
				rs.close();
				rs = stmtGetColumNamesForTable2.executeQuery();
				while (rs.next()) {
					columnNamesDB2.add(rs.getString("COLUMN_NAME"));
				}
				rs.close();
				// search for new columns in existing tables
				final Vector<String> addedColumns = getNewElements(columnNamesDB1, columnNamesDB2);
				if (addedColumns.size() != 0) {
					final Table table = new Table(tableName);
					for (int i = 0; i < addedColumns.size(); i++) {
						stmtGetColumDetailsDB2.setString(1, addedColumns.get(i));
						stmtGetColumDetailsDB2.setString(2, tableName);
						rs = stmtGetColumDetailsDB2.executeQuery();
						while (rs.next()) {
							final Column column = new Column(rs);
							if (!column.isNullable() && column.getDefaultValue() == null) {
								// search for sensible default value and set it
								column.setTmpDefaultValue(getTempDefaultValueForColumn(tableName,
										column));
							}
							table.addColumnToAdd(column);
						}
						rs.close();
					}
					m_changedTables.add(table);
				}
				// search for missing columns in existing tables
				final Vector<String> missingColumns = getMissingElements(columnNamesDB1,
						columnNamesDB2);
				if (missingColumns.size() != 0) {
					final Table table = new Table(tableName);
					for (int i = 0; i < missingColumns.size(); i++) {
						table.addColumnToDrop(new Column(missingColumns.get(i)));
					}
					m_changedTables.add(table);
				}
				// find changed columns
				Table changedTable = null;
				for (int i = 0; i < columnNamesDB1.size(); i++) {
					String colName = columnNamesDB1.get(i);
					if (columnNamesDB2.contains(colName)) {
						// column with that name exists in both dbs
						stmtGetColumDetailsDB1.setString(1, colName);
						stmtGetColumDetailsDB1.setString(2, tableName);
						stmtGetColumDetailsDB2.setString(1, colName);
						stmtGetColumDetailsDB2.setString(2, tableName);
						Column colDB1 = null;
						Column colDB2 = null;
						rs = stmtGetColumDetailsDB1.executeQuery();
						if (rs.next()) {
							colDB1 = new Column(rs);
						}
						rs.close();
						rs = stmtGetColumDetailsDB2.executeQuery();
						if (rs.next()) {
							colDB2 = new Column(rs);
						}
						rs.close();
						if (!colDB1.equals(colDB2)) {
							if (changedTable == null) {
								changedTable = new Table(tableName);
							}
							if (!colDB2.isNullable() && colDB2.getDefaultValue() == null) {
								// search for sensible default value and set it
								colDB2.setTmpDefaultValue(getTempDefaultValueForColumn(tableName,
										colDB2));
							}
							colDB2.setNullHasChanged(colDB2.isNullable() != colDB1.isNullable());
							final String def1 = colDB1.getDefaultValue();
							final String def2 = colDB2.getDefaultValue();
							if (def1 == null && def2 == null) {
								colDB2.setDefaultHasChanged(false);
							} else if (def1 != null && def2 != null) {
								colDB2.setDefaultHasChanged(!def1.equals(def2));
							} else {
								colDB2.setDefaultHasChanged(true);
							}
							changedTable.addColumnToModify(colDB2);
						}

					}
				}
				if (changedTable != null) {
					m_changedTables.add(changedTable);
				}
			}
		}
		stmtGetColumNamesForTable1.close();
		stmtGetColumNamesForTable2.close();
		stmtGetColumDetailsDB1.close();
		stmtGetColumDetailsDB2.close();
	}

	/**
	 * Returns a sensible (applieable) value for the given table and column. The
	 * value can be used as a default value for the column. It is used to
	 * add/modify columns with not null state to tables with existing data.
	 * 
	 * @param tableName
	 * @param column
	 * @return
	 * @throws SQLException
	 */
	private String getTempDefaultValueForColumn(String tableName, Column column)
			throws SQLException {
		String retValue = null;
		// test if it is a foreign key
		String sql = "select col2.Table_Name, col2.Column_Name from User_Cons_Columns col "
				+ "inner join user_constraints con on (col.Constraint_Name=con.Constraint_Name) "
				+ "inner join User_Cons_Columns col2 on (con.R_Constraint_Name=col2.Constraint_Name) "
				+ "where col.table_name='" + tableName + "' and con.constraint_type='R' "
				+ "and col.Column_Name='" + column.getColumnName() + "'";
		ResultSet rs = stmtdb2.executeQuery(sql);
		String fkTableName = "";
		String fkColumnName = "";
		if (rs.next()) {
			fkTableName = rs.getString("Table_Name");
			fkColumnName = rs.getString("Column_Name");
		}
		rs.close();
		if (!"".equals(fkColumnName) && !"".equals(fkTableName)) {
			try {
				// foreign key found - try to get a value from the old db
				// may be an error if fkColumn is new
				sql = "select max(" + fkColumnName + ")  from " + fkTableName;
				rs = stmtdb1.executeQuery(sql);
				if (rs.next()) {
					retValue = rs.getString(1);
				}
				rs.close();
			} catch (SQLException e) {
				// fkColumn doesn't exist in db1
				System.out.println("foreign key column missing in db1: " + fkColumnName
						+ " - table: " + fkTableName);
			}
		}
		if (retValue == null) {
			// no foreign key - check for column type
			if (column.isNumberType()) {
				retValue = "-1";
			} else if (column.isStringType()) {
				retValue = "'N'";
			}
		}
		return retValue;
	}

	/**
	 * Returns a Vector containing the Strings found in objNamesDB1 but not in
	 * objNamesDB2. This could be deleted tables, columns etc. or
	 * customizations.
	 * 
	 * @param objNamesDB1
	 * @param objNamesDB2
	 * @return
	 */
	private Vector<String> getMissingElements(Vector<String> objNamesDB1, Vector<String> objNamesDB2) {
		Vector<String> missingElements = new Vector<String>();
		Iterator iter = objNamesDB1.iterator();
		while (iter.hasNext()) {
			String name = (String) iter.next();
			if (!objNamesDB2.contains(name)) {
				missingElements.add(name);
			}
		}
		return missingElements;
	}

	/**
	 * Returns a Vector containing the Strings found in objNamesDB2 but not in
	 * objNamesDB1.
	 * 
	 * @param objNamesDB1
	 * @param objNamesDB2
	 * @return
	 */
	private Vector<String> getNewElements(Vector<String> objNamesDB1, Vector<String> objNamesDB2) {
		Vector<String> newElements = new Vector<String>();
		Iterator iter = objNamesDB2.iterator();
		while (iter.hasNext()) {
			String name = (String) iter.next();
			if (!objNamesDB1.contains(name)) {
				newElements.add(name);
			}
		}
		return newElements;
	}

	/**
	 * Sorts the generated sql statements by applying them to db1 and prints the
	 * sorted statement list.
	 * 
	 * @throws SQLException
	 */
	private void sortAndPrintSQL() throws SQLException {

		final Vector<String> statements = new Vector<String>(1000, 500);
		final Vector<String> sortedStatements = new Vector<String>(1000, 500);
		for (int i = 0; i < m_newTables.size(); i++) {
			statements.add(m_newTables.get(i).getCreateStatement());
		}
		for (int i = 0; i < m_changedTables.size(); i++) {
			if (m_changedTables.get(i).isAlterAdd()) {
				statements.add(m_changedTables.get(i).getAlterAddStatement());
			} else if (m_changedTables.get(i).isAlterDrop()) {
				statements.add(m_changedTables.get(i).getAlterDropStatement());
			} else if (m_changedTables.get(i).isAlterModify()) {
				statements.add(m_changedTables.get(i).getAlterModifyStatement());
			}
		}
		for (int i = 0; i < m_constraintsToDrop.size(); i++) {
			statements.add(m_constraintsToDrop.get(i).getDropString());
		}
		for (int i = 0; i < m_newConstraints.size(); i++) {
			statements.add(m_newConstraints.get(i).getAlterTableString());
		}
		Vector<String> tempVector = sortStatements(statements);
		for (int i = 0; i < tempVector.size(); i++) {
			sortedStatements.add(tempVector.get(i));
		}
		
		// new data
		sortedStatements.add("COMMIT;");
		sortedStatements.add("SET DEFINE OFF;");
		statements.clear();
		for (int i = 0; i < m_newTableEntry.size(); i++) {
			statements.add(m_newTableEntry.get(i).replaceAll("\n", " "));
		}
		tempVector = sortStatements(statements);
		for (int i = 0; i < tempVector.size(); i++) {
			sortedStatements.add(tempVector.get(i));
		}
		// changed data
		sortedStatements.add("COMMIT;");
		sortedStatements.add("SET DEFINE OFF;");
		statements.clear();
		for (int i = 0; i < m_alterADEntry.size(); i++) {
			statements.add(m_alterADEntry.get(i).replaceAll("\n", " "));
		}
		tempVector = sortStatements(statements);
		for (int i = 0; i < tempVector.size(); i++) {
			sortedStatements.add(tempVector.get(i));
		}
		// data to delete
		sortedStatements.add("COMMIT;");
		sortedStatements.add("SET DEFINE OFF;");
		statements.clear();
		for (int i = 0; i < m_deleteADEntry.size(); i++) {
			statements.add(m_deleteADEntry.get(i).replaceAll("\n", " "));
		}
		tempVector = sortStatements(statements);
		for (int i = 0; i < tempVector.size(); i++) {
			sortedStatements.add(tempVector.get(i));
		}
		

		System.out.println();
		System.out.println("---------------------------");
		System.out.println("--   SCRIPT STARTS HERE!");
		System.out.println("---------------------------");

		System.out.println("-- UNABLE TO APPLY THESE STATEMENTS - START");
		for (int i = 0; i < m_unappliableStatements.size(); i++) {
			System.out.println(m_unappliableStatements.get(i));
		}
		System.out.println("-- UNABLE TO APPLY THESE STATEMENTS - END");

		System.out.println();
		System.out.println("-- NEW/CHANGED TABLES - NEW/CHANGED AD_ENTRIES");
		for (int i = 0; i < sortedStatements.size(); i++) {
			System.out.println(sortedStatements.get(i));
		}

		// no sorting needed
		System.out.println();
		System.out.println("-- NEW VIEWS");
		for (int i = 0; i < m_newViews.size(); i++) {
			System.out.println(m_newViews.get(i).getCreateStatement());
		}
		System.out.println();
		System.out
				.println("-- CHANGED VIEWS - but check them first - don't overwrite your customizations...");
		for (int i = 0; i < m_changedViews.size(); i++) {
			System.out.println(m_changedViews.get(i).getCreateStatement());
		}

		System.out.println();
		System.out.println("-- NEW OR CHANGED FUNCTIONS/PROCEDURES");
		for (int i = 0; i < m_newFunctionStatements.size(); i++) {
			System.out.println(m_newFunctionStatements.get(i));
		}
		System.out.println();
		System.out.println("-- DROP FUNCTIONS/PROCEDURES");
		for (int i = 0; i < m_dropFunctionStatements.size(); i++) {
			System.out.println(m_dropFunctionStatements.get(i));
		}
		System.out.println();
		System.out.println("-- DROP TRIGGERS");
		for (int i = 0; i < m_dropTriggerStatements.size(); i++) {
			System.out.println(m_dropTriggerStatements.get(i));
		}
		System.out.println();
		System.out.println("-- DROP INDEXES");
		for (int i = 0; i < m_dropIndexStatements.size(); i++) {
			System.out.println(m_dropIndexStatements.get(i));
		}
		System.out.println();
		System.out.println("-- NEW OR CHANGED INDEXES");
		for (int i = 0; i < m_newIndexStatements.size(); i++) {
			System.out.println(m_newIndexStatements.get(i));
		}

		System.out.println();
		System.out.println("-- PLEASE CHECK THE SEQUENCES BY HAND - USE:");
		System.out.println("-- select * from user_sequences;");

		System.out.println();
		System.out.println(getUpdateVersionStatement());
		System.out.println("COMMIT;");

	}

	/**
	 * Sorts the given statements by applying them to db1. Returns a Vector
	 * containing the sorted statements.
	 * 
	 * @param statements
	 * @param sortedStatements
	 */
	private Vector<String> sortStatements(final Vector<String> statements) {
		final Vector<String> sortedStatements = new Vector<String>(1000, 500);
		int maxTries = statements.size();
		int attempt = 0;
		while (statements.size() != 0 && attempt <= maxTries) {
			final String statement = statements.firstElement();
			try {
				// there may be not null workaround, and we need to preserve the
				// sequence
				String[] stmts = statement.split(Table.STATEMENT_SEPARATOR);
				for (int j = 0; j < stmts.length; j++) {
					if (!"".equals(stmts[j])) {
						stmtdb1.executeUpdate(stmts[j].substring(0, stmts[j].lastIndexOf(';')));
					}
				}
				sortedStatements.add(statement.replaceAll(Table.STATEMENT_SEPARATOR, ""));
				statements.remove(0);
				maxTries = statements.size();
				attempt = 0;
			} catch (SQLException e) {
				System.out.println(e.getMessage());
				statements.remove(0);
				statements.add(statement);
			}
			attempt++;
		}

		if (statements.size() != 0) {
			for (int i = 0; i < statements.size(); i++) {
				m_unappliableStatements.add(statements.get(i));
			}
		}
		return sortedStatements;
	}

	/**
	 * Prints out the generated sql statements to the console. Old version.
	 * 
	 * @deprecated
	 */
	private void printSQL() {
		System.out.println();
		System.out
				.println("-- please replace all PA_Measure/PA_MeasureCalc related entries with the following lines");
		System.out.println("and place them at the end of the alter tables section");
		System.out.println(getHardcodedStuff());
		System.out.println();
		System.out.println("-- end of the PA_Measure/PA_MeasureCalc replacement lines");
		System.out.println();
		System.out.println("-- USE THE FOLLOWING LINES FOR YOUR MIGRATION SCRIPT:");
		System.out.println("-- INSERT TABLES");
		for (int i = 0; i < m_newTables.size(); i++) {
			System.out.println(m_newTables.get(i).getCreateStatement());
		}
		System.out.println();
		System.out.println("-- ALTER TABLES");
		for (int i = 0; i < m_changedTables.size(); i++) {
			if (m_changedTables.get(i).isAlterAdd()) {
				System.out.println(m_changedTables.get(i).getAlterAddStatement());
			}
		}
		System.out.println("-- PLEASE CHECK THE ADD/MODIFY STATEMENTS:");
		System.out.println("-- If you find a 'BEWARE' first check if the table has some data");
		System.out.println("-- if not, you can ignort the 'BEWARE', if the table has data");
		System.out.println("-- you might have to set a temporary default value for the column");
		System.out.println("-- (if not all rows have a value for the modied column)");

		System.out.println("-- Sample: 		ALTER TABLE TEST ADD ( columnname NOT NULL);");
		System.out
				.println("-- first set default value (for existing data), than remove it (for new data)");
		System.out
				.println("-- change to:	ALTER TABLE TEST ADD ( columnname DEFAULT 'tempDefault' NOT NULL);");
		System.out.println("-- 				ALTER TABLE TEST MODIFY ( columnname NULL);");
		System.out.println("-- 				ALTER TABLE TEST MODIFY ( columnname NOT NULL);");
		System.out.println();
		System.out.println("-- FOR MODIFY STATEMENTS");
		System.out.println("-- just set a value for all existing rows");
		System.out.println();
		System.out.println("-- Sample: 		ALTER TABLE TEST MODIFY ( columnname not null);");
		System.out.println("-- insert an update statement before the alter statement");
		System.out.println("-- insert before:	UPDATE TEST SET columnname 'myDefault';");

		for (int i = 0; i < m_changedTables.size(); i++) {
			if (m_changedTables.get(i).isAlterModify()) {
				System.out.println(m_changedTables.get(i).getAlterModifyStatement());
			}
		}
		System.out.println("-- PLEASE CHECK THE FOLLOWING COLUMNS - DON'T DROP CUSTOMIZATIONS");
		for (int i = 0; i < m_changedTables.size(); i++) {
			if (m_changedTables.get(i).isAlterDrop()) {
				System.out.println(m_changedTables.get(i).getAlterDropStatement());
			}
		}
		System.out.println();
		System.out.println("-- DROP TABLES - but check them first - don't drop customizations...");
		for (int i = 0; i < m_tablesToDrop.size(); i++) {
			System.out.println(m_tablesToDrop.get(i).getDropStatement());
		}

		System.out.println();
		System.out.println();
		System.out.println("-- PUT THE FOLLOWING LINES INTO A NEW FILE");
		System.out.println();

		System.out.println();
		System.out.println("-- DROP CONSTRAINTS");
		for (int i = 0; i < m_constraintsToDrop.size(); i++) {
			System.out.println(m_constraintsToDrop.get(i).getDropString());
		}
		System.out.println();
		System.out.println("-- ADD/RECREATE CONSTRAINTS");
		for (int i = m_newConstraints.size() - 1; i >= 0; i--) {
			System.out.println(m_newConstraints.get(i).getAlterTableString());
		}

		System.out.println();
		System.out.println();
		System.out.println("-- PUT THE FOLLOWING LINES INTO A NEW FILE");
		System.out.println();

		System.out.println();
		System.out.println("-- NEW AD_* ENTRIES AND ROWS OF ADDED TABLES");
		System.out.println();
		System.out.println("SET DEFINE OFF;");
		System.out.println();
		Collections.sort(m_newTableEntry, new AD_Comparator());
		for (int i = 0; i < m_newTableEntry.size(); i++) {
			System.out.println(m_newTableEntry.get(i).replaceAll("\n", " "));
		}
		System.out.println();
		System.out.println("-- CHANGED AD_* ENTRIES");
		Collections.sort(m_alterADEntry);
		for (int i = 0; i < m_alterADEntry.size(); i++) {
			System.out.println(m_alterADEntry.get(i).replaceAll("\n", " "));
		}

		System.out.println();
		System.out.println();
		System.out.println("-- PUT THE FOLLOWING LINES INTO A NEW FILE");
		System.out.println();

		System.out.println();
		System.out.println("-- NEW VIEWS");
		for (int i = 0; i < m_newViews.size(); i++) {
			System.out.println(m_newViews.get(i).getCreateStatement());
		}
		System.out.println();
		System.out
				.println("-- CHANGED VIEWS - but check them first - don't overwrite your customizations...");
		for (int i = 0; i < m_changedViews.size(); i++) {
			System.out.println(m_changedViews.get(i).getCreateStatement());
		}

		System.out.println();
		System.out.println("-- NEW OR CHANGED FUNCTIONS/PROCEDURES");
		for (int i = 0; i < m_newFunctionStatements.size(); i++) {
			System.out.println(m_newFunctionStatements.get(i));
		}

		System.out.println();
		System.out.println("-- DROP FUNCTIONS/PROCEDURES");
		for (int i = 0; i < m_dropFunctionStatements.size(); i++) {
			System.out.println(m_dropFunctionStatements.get(i));
		}

		System.out.println();
		System.out.println("-- DROP TRIGGERS - BUT DON'T DELETE CUSTOMIZATIONS");
		for (int i = 0; i < m_dropTriggerStatements.size(); i++) {
			System.out.println(m_dropTriggerStatements.get(i));
		}
		System.out.println();
		System.out.println("-- DROP INDEXES - BUT DON'T DELETE CUSTOMIZATIONS");
		for (int i = 0; i < m_dropIndexStatements.size(); i++) {
			System.out.println(m_dropIndexStatements.get(i));
		}
		System.out.println();
		System.out.println("-- NEW OR CHANGED INDEXES");
		for (int i = 0; i < m_newIndexStatements.size(); i++) {
			System.out.println(m_newIndexStatements.get(i));
		}

		System.out.println();
		System.out.println("-- PLEASE CHECK THE SEQUENCES BY HAND - USE:");
		System.out.println("-- select * from user_sequences;");

		System.out.println();
		System.out.println(getUpdateVersionStatement());
		System.out.println("COMMIT;");

	}

	/**
	 * Compares the constraint, if there are differences drop the old and create
	 * a new one.
	 * 
	 * @param name
	 * @param stat1
	 * @param stat2
	 * @throws SQLException
	 */
	private void compareConstraints() throws SQLException {
		final Vector<Constraint> constraintsDB1 = new Vector<Constraint>();
		final Vector<Constraint> constraintsDB2 = new Vector<Constraint>();
		// get all constrains but not the U(nique) and SYS_* ones
		// SYS_* constraints - name is autocreated for the not null and check
		// constraints without name
		// They are handled by the alter table statements
		String sql = "select * from user_constraints where constraint_name not like 'SYS_%' and constraint_name not like 'BIN%' and constraint_type != 'U'";

		ResultSet rs = stmtdb1.executeQuery(sql);
		while (rs.next()) {
			constraintsDB1.add(new Constraint(rs.getString("CONSTRAINT_NAME"), rs
					.getString("TABLE_NAME")));
		}
		rs.close();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			constraintsDB2.add(new Constraint(rs.getString("CONSTRAINT_NAME"), rs
					.getString("TABLE_NAME")));
		}
		rs.close();
		// find missing constraints - don't delete them might be customizations,
		// just alert them
		System.out.println("searching constraints to drop...");

		Vector<Constraint> constraintsToDrop = new Vector<Constraint>();
		Vector<Constraint> duplicatedConstraints = new Vector<Constraint>();
		for (int i = 0; i < constraintsDB1.size(); i++) {
			String n1 = constraintsDB1.get(i).getName();
			boolean found = false;
			for (int j = 0; j < constraintsDB2.size(); j++) {
				String n2 = constraintsDB2.get(j).getName();
				if (n2.equals(n1)) {
					found = true;
					break;
				}
			}
			if (!found) {
				// constraint in db1 but not found in db2
				constraintsToDrop.add(constraintsDB1.get(i));
			} else {
				// check for changes
				duplicatedConstraints.add(constraintsDB1.get(i));
			}
		}

		for (int i = 0; i < constraintsToDrop.size(); i++) {
			m_constraintsToDrop.add(constraintsToDrop.get(i));
		}
		System.out.println("searching new constraints...");
		// find new constraints
		for (int i = 0; i < constraintsDB2.size(); i++) {
			String n2 = constraintsDB2.get(i).getName();
			boolean found = false;
			for (int j = 0; j < constraintsDB1.size(); j++) {
				String n1 = constraintsDB1.get(j).getName();
				if (n2.equals(n1)) {
					found = true;
					break;
				}
			}
			if (!found) {
				m_newConstraints.add(createNewConstraint(constraintsDB2.get(i).getName()));
			}
		}

		// old version
		for (int i = 0; i < duplicatedConstraints.size(); i++) {

			String name = duplicatedConstraints.get(i).getName();
			sql = "select Constraint_Type, Table_Name,"
					+ "Search_Condition, R_Constraint_Name, Delete_Rule"
					+ " from User_Constraints where Constraint_Name='" + name + "'";
			String sqlColumn = "select * from User_Cons_Columns where Constraint_Name='" + name
					+ "'";
			rs = stmtdb2.executeQuery(sql);
			String tableName2 = "";
			String constraintType2 = "";
			String condition2 = "";
			String rConstraintName2 = "";
			String deleteRule2 = "";
			String column2 = "";
			if (rs.next()) {
				constraintType2 = rs.getString("Constraint_Type");
				tableName2 = rs.getString("Table_Name");
				condition2 = rs.getString("Search_Condition");
				rConstraintName2 = rs.getString("R_Constraint_Name");
				deleteRule2 = rs.getString("Delete_Rule");
			}
			rs.close();
			rs = stmtdb2.executeQuery(sqlColumn);
			if (rs.next()) {
				column2 = rs.getString("Column_Name");
			}
			rs.close();
			rs = stmtdb1.executeQuery(sql);
			String tableName1 = "";
			String constraintType1 = "";
			String condition1 = "";
			String rConstraintName1 = "";
			String deleteRule1 = "";
			String column1 = "";
			if (rs.next()) {
				constraintType1 = rs.getString("Constraint_Type");
				tableName1 = rs.getString("Table_Name");
				condition1 = rs.getString("Search_Condition");
				rConstraintName1 = rs.getString("R_Constraint_Name");
				deleteRule1 = rs.getString("Delete_Rule");
			}
			rs.close();
			rs = stmtdb1.executeQuery(sqlColumn);
			if (rs.next()) {
				column1 = rs.getString("Column_Name");
			}
			rs.close();
			if (condition1 == null) {
				condition1 = "";
			}
			if (rConstraintName1 == null) {
				rConstraintName1 = "";
			}
			if (deleteRule1 == null) {
				deleteRule1 = "";
			}
			if (column1 == null) {
				column1 = "";
			}
			if (condition2 == null) {
				condition2 = "";
			}
			if (rConstraintName2 == null) {
				rConstraintName2 = "";
			}
			if (deleteRule2 == null) {
				deleteRule2 = "";
			}
			if (column2 == null) {
				column2 = "";
			}
			if (tableName1.equals(tableName2) && constraintType1.equals(constraintType2)
					&& condition1.equals(condition2) && rConstraintName1.equals(rConstraintName2)
					&& deleteRule1.equals(deleteRule2) && column1.equals(column2)) {
				// seems to be equal...
			} else {
				m_constraintsToDrop.add(duplicatedConstraints.get(i));
				m_newConstraints.add(createNewConstraint(name));
			}

		}
		addMissingSysConstraints();

	}

	/**
	 * Creates check constrains found in db2 but not in db1 and adds them to the
	 * global m_newConstraints Vector.
	 */
	private void addMissingSysConstraints() {
		String sql2 = "";
		final Vector<String> newConstraintNames = new Vector<String>();
		try {
			String sql = "select * from user_constraints where constraint_name like 'SYS_%' and constraint_type='C'";
			ResultSet rs2 = stmtdb2.executeQuery(sql);
			while (rs2.next()) {
				final String searchCondition = rs2.getString("SEARCH_CONDITION");
				if (searchCondition == null) {
					continue;
				} else if (searchCondition.toUpperCase().indexOf("IS NOT NULL") != -1) {
					// not null constraints are handled by alter table
					continue;
				}
				final String tableName = rs2.getString("TABLE_NAME");

				sql2 = "select * from user_constraints where table_name='" + tableName
						+ "' and search_condition is not null";
				ResultSet rs1 = stmtdb1.executeQuery(sql2);
				boolean found = false;
				while (rs1.next()) {
					if (searchCondition.equals(rs1.getString("SEARCH_CONDITION"))) {
						found = true;
						continue;
					}
				}
				rs1.close();
				if (!found) {
					newConstraintNames.add(rs2.getString("CONSTRAINT_NAME"));
				}
			}
			rs2.close();
			for (int i = 0; i < newConstraintNames.size(); i++) {
				m_newConstraints.add(createNewConstraint(newConstraintNames.get(i)));
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println(sql2);
		}
	}

	/**
	 * Creates a new constraint from the data found for the constraint with the
	 * given name in db2.
	 * 
	 * @param stmtdb2
	 * @param name
	 * @throws SQLException
	 */
	private Constraint createNewConstraint(String name) throws SQLException {
		ResultSet rs = stmtdb2.executeQuery("select Constraint_Type, Table_Name,"
				+ "Search_Condition, R_Constraint_Name, Delete_Rule"
				+ " from User_Constraints where Constraint_Name='" + name + "'");
		String tableName = "";
		String constraintType = "";
		String condition = "";
		String rConstraintName = "";
		String deleteRule = "";
		String column = "";
		if (rs.next()) {
			constraintType = rs.getString("Constraint_Type");
			tableName = rs.getString("Table_Name");
			condition = rs.getString("Search_Condition");
			rConstraintName = rs.getString("R_Constraint_Name");
			deleteRule = rs.getString("Delete_Rule");
		}
		rs.close();

		if (constraintType.equals(Constraint.FOREIGN_KEY)) {
			Constraint refConstraint = null;
			rs = stmtdb2.executeQuery("select * from User_Cons_Columns where Constraint_Name='"
					+ name + "'");
			if (rs.next()) {
				column = rs.getString("Column_Name");
			}
			rs.close();
			rs = stmtdb2.executeQuery("select * from User_Cons_Columns where Constraint_Name='"
					+ rConstraintName + "'");
			if (rs.next()) {
				String refColumn = rs.getString("Column_Name");
				String refTableName = rs.getString("Table_Name");
				refConstraint = new Constraint(rConstraintName, refTableName);
				refConstraint.addColumnName(refColumn);
				refConstraint.setDeleteRule(deleteRule);
			}
			rs.close();
			Constraint c = new Constraint(name, tableName);
			c.setType(Constraint.FOREIGN_KEY);
			c.addColumnName(column);
			c.setRConstraint(refConstraint);
			c.addColumnName(column);
			c.setDeleteRule(deleteRule);
			return (c);
		} else if (constraintType.equals(Constraint.PRIMARY_KEY)) {
			rs = stmtdb2.executeQuery("select * from User_Cons_Columns where Constraint_Name='"
					+ name + "'");
			Constraint c = new Constraint(name, tableName);
			c.setType(Constraint.PRIMARY_KEY);
			c.setDeleteRule(deleteRule);
			
			while (rs.next()) {
				c.addColumnName(rs.getString("Column_Name"));
			}
			rs.close();
			return (c);
		} else if (constraintType.equals(Constraint.CHECK)) {
			rs = stmtdb2.executeQuery("select * from User_Cons_Columns where Constraint_Name='"
					+ name + "'");
			if (rs.next()) {
				column = rs.getString("Column_Name");
			}
			rs.close();
			Constraint c = new Constraint(name, tableName);
			c.setType(Constraint.CHECK);
			c.addColumnName(column);
			c.setCheckCondition(condition);
			c.setDeleteRule(deleteRule);
			return (c);
		}
		return null;
	}

	/**
	 * Compares the data in the AD_% tables. Looks for new elements only.
	 * 
	 * @param stmtdb1
	 * @param stmtdb2
	 * @throws SQLException
	 */
	private void compareADElements() throws SQLException {
		final String TABLE_DOES_NOT_EXITST = "ORA-00942";
		final Vector<String> adTableNames2 = new Vector<String>();
		String sql = "select table_name from user_tables";// where table_name like 'AD_%'";

		ResultSet rs = stmtdb2.executeQuery(sql);

		while (rs.next()) {
			final String tableName = rs.getString("TABLE_NAME");

			if (tableName.endsWith("_TRL")) {
				continue;
			}
			adTableNames2.add(tableName);
		}
		rs.close();

		for (int i = 0; i < adTableNames2.size(); i++) {
			String tableName = adTableNames2.get(i);
			boolean addClientAccess = false;

			if (tableName.equals("AD_WINDOW_ACCESS") || tableName.equals("AD_PROCESS_ACCESS")
					|| tableName.equals("AD_FORM_ACCESS")) {
				addClientAccess = true;
			}

			// get all column names
			final Vector<Column> columns1 = new Vector<Column>();
			final Vector<Column> columns2 = new Vector<Column>();
			sql = "select * from user_tab_columns where table_name='" + tableName
					+ "' order by column_id";
			rs = stmtdb1.executeQuery(sql);
			while (rs.next()) {
				columns1.add(new Column(rs));
			}
			rs.close();
			rs = stmtdb2.executeQuery(sql);
			while (rs.next()) {
				columns2.add(new Column(rs));
			}
			rs.close();
			// get keycolumns for table
			sql = "select col.column_name from user_constraints constr "
					+ "inner join User_Cons_Columns col ON (col.constraint_name = constr.constraint_name) "
					+ "where constr.constraint_type='P' and constr.table_name = '" + tableName
					+ "'";
			final Vector<String> keycolumns = new Vector<String>();
			rs = stmtdb2.executeQuery(sql);

			// create select statement for table
			sql = "select * from " + tableName + " where ";
			if (rs.next()) {
				sql += rs.getString("COLUMN_NAME");
				sql += " <1000000 ";
				keycolumns.add(rs.getString("COLUMN_NAME"));
			} else {
				// no keyColumn found
				System.out.println("Please check table " + tableName
						+ " for new data and add it by hand");
				continue;
			}
			while (rs.next()) {
				sql += " and " + rs.getString("COLUMN_NAME");
				sql += " <1000000 ";
				keycolumns.add(rs.getString("COLUMN_NAME"));
			}
			rs.close();

			Vector<ADDataElement> dataElements1 = new Vector<ADDataElement>();
			Vector<ADDataElement> dataElements2 = new Vector<ADDataElement>();
			try {
				// get data from db1 and db2 and compare them...

				rs = stmtdb2.executeQuery(sql);
				while (rs.next()) {
					final ADDataElement data = new ADDataElement();
					for (int j = 0; j < columns2.size(); j++) {
						final String colName = columns2.get(j).getColumnName();
						data.addColumnAndValue(colName, rs.getString(colName));
					}
					dataElements2.add(data);
				}
				rs.close();
				// error if new colums was added and is key column...
				rs = stmtdb1.executeQuery(sql);
				while (rs.next()) {
					final ADDataElement data = new ADDataElement();
					for (int j = 0; j < columns1.size(); j++) {
						final String colName = columns1.get(j).getColumnName();
						data.addColumnAndValue(colName, rs.getString(colName));
					}
					dataElements1.add(data);
				}
				rs.close();
				// got all data - lets compare them (will take some time...)
				// new entry? search for data with same keyValues
				
				//TODO: drop entries missing in db2...
				try {
					//find elements to drop
					for(int j = 0; j < dataElements1.size(); j++) {
						boolean found = false;
						final ADDataElement data1 = dataElements1.get(j);
						sql = "select * from " + tableName + " where ";
						for (int m = 0; m < keycolumns.size(); m++) {
							if (m != 0) {
								sql += " and ";
							}
							sql += keycolumns.get(m) + "=" + data1.getValueForColumn(keycolumns.get(m));
						}
						rs = stmtdb2.executeQuery(sql);
						if (rs.next()) {
							found = true;
						}
						rs.close();
						if(!found){
							//data exist in db1 but no longer in db2 - delete it
							createDeleteTableEntry(tableName, data1, keycolumns, columns1);
						}
					}
				} catch (SQLException e1) {
					// if keyColumns have changed...
					System.out.println(e1.getMessage() + " - on searching data to drop for table: " + tableName);
				}

				for (int j = 0; j < dataElements2.size(); j++) {
					boolean found = false;
					final ADDataElement data2 = dataElements2.get(j);
					sql = "select * from " + tableName + " where ";
					for (int m = 0; m < keycolumns.size(); m++) {
						if (m != 0) {
							sql += " and ";
						}
						sql += keycolumns.get(m) + "=" + data2.getValueForColumn(keycolumns.get(m));
					}
					rs = stmtdb1.executeQuery(sql);
					if (rs.next()) {
						found = true;
					}
					rs.close();
					if (!found) {
						createNewTableEntry(tableName, data2, keycolumns, columns2);
						if (addClientAccess && AD_CLIENT_ID != null && AD_ROLE_ID != null) {
							data2.setValueForColumn("AD_CLIENT_ID", AD_CLIENT_ID);
							data2.setValueForColumn("AD_ROLE_ID", AD_ROLE_ID);
							createNewTableEntry(tableName, data2, keycolumns, columns2);
						}
					} else {
						// could be changed...
						// compare Strings and Numbers but not Dates like
						// created/updated
						// because they are changed every version...
						sql = "select * from " + tableName + " where ";
						boolean and = false;
						for (int m = 0; m < columns2.size(); m++) {
							if (!columns1.contains(columns2.get(m))) {
								// column added in db2
								continue;
							}
							String value = data2.getValueForColumn(columns2.get(m).getColumnName());
							if (value != null && value.indexOf('\'') != -1) {
								value = value.replaceAll("'", "''");
							}
							if (and) {
								sql += " and ";
							}
							if (value == null) {
								sql += columns2.get(m).getColumnName() + " is null ";
								and = true;
								continue;
							}
							if (columns2.get(m).isStringType()) {

								sql += columns2.get(m).getColumnName() + "='" + value + "'";
								and = true;
							} else if (columns2.get(m).isNumberType()) {
								sql += columns2.get(m).getColumnName() + "="
										+ data2.getValueForColumn(columns2.get(m).getColumnName());
								and = true;
							} else {
								and = false;
							}

						}
						try {
							found = false;
							// System.out.println(sql);
							rs = stmtdb1.executeQuery(sql);

							if (rs.next()) {
								found = true;
							}
							rs.close();
							if (!found) {
								updateADEntry(tableName, data2, keycolumns, columns2);
							}
						} catch (SQLException e) {
							// corrupt sql statement because sometimes Strings
							// contains '
							if (e.getMessage().startsWith("ORA-00933")
									|| e.getMessage().startsWith("ORA-01722")) {
								// we could miss some changed ad elements but
								// never mind...
								;
							} else {
								System.out.println(e.getMessage() + ":");
								System.out.println(sql);
							}
						}

					}
				}

			} catch (SQLException e) {
				if (e.getMessage().startsWith(TABLE_DOES_NOT_EXITST)) {
					// new ad_* table, add all elements from db2
					for (int j = 0; j < dataElements2.size(); j++) {
						createNewTableEntry(tableName, dataElements2.get(j), keycolumns, columns2);
					}
				} else {
					System.out.println(e.getMessage() + " - for table " + tableName);
				}
			}

		}
	}

	/**
	 * Creates a delete from table statement for the given entry and adds it to the 
	 * global m_deleteADEntry Vector.
	 * @param tableName
	 * @param data1
	 * @param keycolumns
	 * @param columns1
	 */
	private void createDeleteTableEntry(String tableName, ADDataElement data1, Vector<String> keycolumns, Vector<Column> columns1) {
		String alterStatement = "DELETE FROM " + tableName + " WHERE ";
		boolean and = false;
		for (int i = 0; i < columns1.size(); i++) {
			final Column column = columns1.get(i);
			final String columnName = column.getColumnName();
			if (keycolumns.contains(columnName)) {
				if (and) {
					alterStatement += " AND ";
				}
				and = true;
				if (data1.getValueForColumn(columnName) == null) {
					alterStatement += column.getColumnName() + " is null ";
					continue;
				}
				alterStatement += column.getColumnName() + "=";
				if (column.isStringType()) {
					alterStatement += "'"
							+ data1.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else {
					alterStatement += data1.getValueForColumn(columnName);
				}
			}
		}
		alterStatement += ";";
		m_deleteADEntry.add(alterStatement);
		
	}

	/**
	 * Creates an update statement for the AD_* entry identified by the given
	 * tableName, ADDataElement and keycolumns. And adds them to the global
	 * m_alterADEntry Vector.
	 * 
	 * @param tableName
	 * @param data2
	 * @param keycolumns
	 * @param columns2
	 */
	private void updateADEntry(String tableName, ADDataElement data2, Vector<String> keycolumns,
			Vector<Column> columns2) {
		String alterStatement = "UPDATE " + tableName + " SET ";
		boolean comma = false;
		for (int i = 0; i < columns2.size(); i++) {
			final Column column = columns2.get(i);
			if (!keycolumns.contains(column.getColumnName())
					&& !"DATE".equals(column.getDataType())) {
				if (comma) {
					alterStatement += ",";
				}
				comma = true;
				final String columnName = column.getColumnName();
				alterStatement += column.getColumnName() + "=";
				if (column.isStringType()) {
					if (data2.getValueForColumn(columnName) == null) {
						alterStatement += "null";
						continue;
					}
					alterStatement += "'"
							+ data2.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else {
					alterStatement += data2.getValueForColumn(columnName);
				}
			}
		}
		alterStatement += " WHERE ";
		boolean and = false;
		for (int i = 0; i < columns2.size(); i++) {

			final Column column = columns2.get(i);
			final String columnName = column.getColumnName();
			if (keycolumns.contains(columnName)) {
				if (and) {
					alterStatement += " AND ";
				}
				and = true;
				if (data2.getValueForColumn(columnName) == null) {
					alterStatement += column.getColumnName() + " is null ";
					continue;
				}
				alterStatement += column.getColumnName() + "=";
				if (column.isStringType()) {
					alterStatement += "'"
							+ data2.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else {
					alterStatement += data2.getValueForColumn(columnName);
				}
			}
		}
		alterStatement += ";";
		m_alterADEntry.add(alterStatement);
	}

	/**
	 * Creates an insert statement for the data identified by the given
	 * tableName, keyColumnName and keyColumValue and adds them to the global
	 * m_newTableEntry Vector. It's a row in an AD_% table found in db2 but not
	 * in db1 or a row of a new table.
	 * 
	 * @param tableName
	 * @param data2
	 * @param keyColumnNames2
	 * @throws SQLException
	 */
	private void createNewTableEntry(String tableName, ADDataElement data2,
			Vector<String> keyColumnNames2, Vector<Column> columns) throws SQLException {

		String insertStatement = "INSERT INTO " + tableName + "(";
		for (int i = 0; i < columns.size(); i++) {
			if (i != 0) {
				insertStatement += ",";
			}
			insertStatement += columns.get(i).getColumnName();
		}

		insertStatement += ")values(";

		for (int i = 0; i < columns.size(); i++) {
			if (i != 0) {
				insertStatement += ",";
			}
			// get the column (type/name) for the actual dataelement
			String type = columns.get(i).getDataType();
			String columnName = columns.get(i).getColumnName();
			int precision = columns.get(i).getDataPrecision();
			int scale = columns.get(i).getDataScale();
			if (data2.getValueForColumn(columnName) == null) {
				insertStatement += "null";
			} else {
				if (type.equals("BLOB")) {
					// I'm not shure but I think just do it with ''
					insertStatement += "'"
							+ data2.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else if (type.equals("RAW")) {
					// I don't know.... //TODO
				} else if (type.equals("CLOB")) {
					insertStatement += "'"
							+ data2.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else if (type.equals("CHAR") || type.equals("NCHAR") || type.equals("NVARCHAR2")
						|| type.equals("VARCHAR2")) {
					insertStatement += "'"
							+ data2.getValueForColumn(columnName).replaceAll("'", "''") + "'";
				} else if (type.equals("DATE")) {
					String date = data2.getValueForColumn(columnName);
					if (date.indexOf(' ') != -1) {
						date = date.substring(0, date.indexOf(' '));
					}
					insertStatement += "to_date('" + date + "','" + TIME_FORMAT + "')";
				} else if (type.equals("NUMBER")) {
					if (scale == 0) {
						insertStatement += data2.getValueForColumn(columnName);
					} else {
						insertStatement += data2.getValueForColumn(columnName);
					}
				}
			}

		}

		insertStatement += ");";
		m_newTableEntry.add(insertStatement);
	}

	/**
	 * Creates insert statements for every row of the given table and adds them
	 * to the global m_newTableEntry Vector.
	 * 
	 * @param tableName
	 * @throws SQLException
	 */
	private void createEntriesForTable(String tableName) throws SQLException {

		// get all column names
		final Vector<Column> columns2 = new Vector<Column>();
		String sql = "select * from user_tab_columns where table_name='" + tableName
				+ "' order by column_id";
		ResultSet rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			columns2.add(new Column(rs));
		}
		rs.close();

		// get keycolumns for table
		sql = "select col.column_name from user_constraints constr "
				+ "inner join User_Cons_Columns col ON (col.constraint_name = constr.constraint_name) "
				+ "where constr.constraint_type='P' and constr.table_name = '" + tableName + "'";
		final Vector<String> keycolumns = new Vector<String>();
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			keycolumns.add(rs.getString("COLUMN_NAME"));
		}
		rs.close();
		// create select statement for table
		sql = "select * from " + tableName;

		Vector<ADDataElement> dataElements2 = new Vector<ADDataElement>();

		// get data from db2
		rs = stmtdb2.executeQuery(sql);
		while (rs.next()) {
			final ADDataElement data = new ADDataElement();
			for (int i = 0; i < columns2.size(); i++) {
				final String colName = columns2.get(i).getColumnName();
				data.addColumnAndValue(colName, rs.getString(colName));
			}
			dataElements2.add(data);
		}
		rs.close();

		for (int i = 0; i < dataElements2.size(); i++) {
			try {
				createNewTableEntry(tableName, dataElements2.get(i), keycolumns, columns2);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	/**
	 * Some tables cannot be created the normal way because of to many
	 * constraints. This code is tested for 253b.
	 * 
	 * @deprecated
	 * @return
	 */
	private String getHardcodedStuff() {
		StringBuffer buffer = new StringBuffer();
		buffer = buffer
				.append("--  BEWARE: ALL ENTRIES IN PA_MEASURE AND PA_MEASURECALC ARE DELETED \n")
				.append("DELETE FROM PA_MEASURE;\n")
				.append("DELETE FROM PA_MEASURECALC;\n")
				.append("\n")
				.append("  ALTER TABLE PA_MEASURECALC \n")
				.append("  ADD (	AD_TABLE_ID NUMBER(10,0) NOT NULL ENABLE, \n")
				.append("	KEYCOLUMN NVARCHAR2(60) NOT NULL ENABLE, \n")
				.append("	ENTITYTYPE VARCHAR2(4 BYTE) NOT NULL ENABLE\n")
				.append("   ) ;\n")
				.append("\n")
				.append("--  INSERTING into PA_MEASURECALC \n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (100,0,0,'Y',to_date('26.04.01','DD.MM.RR'),0,to_date('01.01.06','DD.MM.RR'),100,'Invoiced Net Revenue','Invoiced net revenue, without tax and charges','SELECT SUM(il.LineNetAmt) \n")
				.append("FROM RV_C_Invoice C_Invoice\n")
				.append(
						"  INNER JOIN RV_C_InvoiceLine il ON (C_Invoice.C_Invoice_ID=il.C_Invoice_ID)','WHERE C_Invoice.IsSOTrx=''Y'' AND C_Invoice.Processed=''Y''','C_Invoice.DateInvoiced','C_Invoice.AD_Org_ID','C_Invoice.C_BPartner_ID','il.M_Product_ID',318,'C_Invoice.C_Invoice_ID','D');\n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (101,0,0,'Y',to_date('26.04.01','DD.MM.RR'),0,to_date('01.01.06','DD.MM.RR'),100,'Invoiced Gross Revenue','Invoice gross amount including tax; Does not allow selection by product (Category)','SELECT SUM(GrandTotal) \n")
				.append(
						"FROM RV_C_Invoice C_Invoice','WHERE IsSOTrx=''Y'' AND Processed=''Y''','DateInvoiced','AD_Org_ID','C_BPartner_ID',null,318,'C_Invoice_ID','D');\n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (102,0,0,'Y',to_date('26.04.01','DD.MM.RR'),0,to_date('01.01.06','DD.MM.RR'),100,'Invoiced Sales Margin','Difference between Limit and Actual price','SELECT SUM((il.PriceActual-il.PriceLimit)*QtyInvoiced) \n")
				.append("FROM RV_C_Invoice C_Invoice\n")
				.append(
						"  INNER JOIN RV_C_InvoiceLine il ON (C_Invoice.C_Invoice_ID=il.C_Invoice_ID)','WHERE C_Invoice.IsSOTrx=''Y'' AND C_Invoice.Processed=''Y''','C_Invoice.DateInvoiced','C_Invoice.AD_Org_ID','C_Invoice.C_BPartner_ID','il.M_Product_ID',318,'C_Invoice.C_Invoice_ID','D');\n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (103,0,0,'Y',to_date('26.04.01','DD.MM.RR'),0,to_date('25.12.05','DD.MM.RR'),100,'Number of Customers','Number of (new) customers','SELECT COUNT(*) \n")
				.append(
						"FROM C_BPartner','WHERE IsCustomer=''Y''','Created','AD_Org_ID','C_BPartner_ID',null,291,'C_BPartner_ID','D');\n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (104,0,0,'Y',to_date('25.12.05','DD.MM.RR'),100,to_date('01.01.06','DD.MM.RR'),100,'Invoiced Paid Quantities','Invoiced paid quantities','SELECT SUM(il.QtyInvoiced) \n")
				.append("FROM RV_C_Invoice C_Invoice\n")
				.append(
						"  INNER JOIN RV_C_InvoiceLine il ON (C_Invoice.C_Invoice_ID=il.C_Invoice_ID)','WHERE C_Invoice.IsSOTrx=''Y'' AND C_Invoice.Processed=''Y'' AND C_Invoice.IsPaid=''Y''\n")
				.append(
						"','C_Invoice.DateInvoiced','C_Invoice.AD_Org_ID','C_Invoice.C_BPartner_ID','il.M_Product_ID',318,'C_Invoice.C_Invoice_ID','D');\n")
				.append(
						"INSERT INTO PA_MEASURECALC (PA_MEASURECALC_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,SELECTCLAUSE,WHERECLAUSE,DATECOLUMN,ORGCOLUMN,BPARTNERCOLUMN,PRODUCTCOLUMN,AD_TABLE_ID,KEYCOLUMN,ENTITYTYPE) values (105,0,0,'Y',to_date('01.01.06','DD.MM.RR'),100,to_date('01.01.06','DD.MM.RR'),100,'Open Invoice Amount','Open Invoice Amount  in Accounting Currency','SELECT COALESCE(SUM(currencyBase(invoiceOpen(C_Invoice_ID, C_InvoicePaySchedule_ID),C_Currency_ID, DateAcct, AD_Client_ID, AD_Org_ID)),0)\n")
				.append(
						"FROM C_Invoice_v C_Invoice','WHERE IsSOTrx=''Y'' AND Processed=''Y''','DateInvoiced','AD_Org_ID','C_BPartner_ID',null,318,'C_Invoice_ID','D');\n")
				.append("\n")
				.append("\n")
				.append("  ALTER TABLE PA_MEASURE \n")
				.append("  ADD (	PA_BENCHMARK_ID NUMBER(10,0), \n")
				.append("	PA_RATIO_ID NUMBER(10,0), \n")
				.append("	PA_HIERARCHY_ID NUMBER(10,0), \n")
				.append("	MEASUREDATATYPE CHAR(1 BYTE) NOT NULL ENABLE, \n")
				.append("	R_REQUESTTYPE_ID NUMBER(10,0), \n")
				.append("	C_PROJECTTYPE_ID NUMBER(10,0)\n")
				.append("   ) ;\n")
				.append("   \n")
				.append(" ALTER TABLE  PA_MEASURE\n")
				.append(" ADD CONSTRAINT  PABENCHMARK_PAMEASURE FOREIGN KEY (PA_BENCHMARK_ID)\n")
				.append("   	  REFERENCES PA_BENCHMARK (PA_BENCHMARK_ID); \n")
				.append(" ALTER TABLE  PA_MEASURE\n")
				.append(" ADD CONSTRAINT PAHIERARCHY_PAMEASURE FOREIGN KEY (PA_HIERARCHY_ID)\n")
				.append("   	  REFERENCES PA_HIERARCHY (PA_HIERARCHY_ID);  \n")
				.append(" ALTER TABLE  PA_MEASURE\n")
				.append(" ADD CONSTRAINT PARATIO_PAMEASURE FOREIGN KEY (PA_RATIO_ID)\n")
				.append("	  REFERENCES PA_RATIO (PA_RATIO_ID);\n")
				.append("   \n")
				.append("--  INSERTING into PA_MEASURE \n")
				.append(
						"INSERT INTO PA_MEASURE (PA_MEASURE_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,MEASURETYPE,MANUALACTUAL,MANUALNOTE,CALCULATIONCLASS,PA_MEASURECALC_ID,PA_BENCHMARK_ID,PA_RATIO_ID,PA_HIERARCHY_ID,MEASUREDATATYPE,R_REQUESTTYPE_ID,C_PROJECTTYPE_ID) values (102,11,0,'Y',to_date('01.01.06','DD.MM.RR'),100,to_date('01.01.06','DD.MM.RR'),100,'Open Invoice Amount',null,'C',0,null,null,105,null,null,null,'S',null,null);\n")
				.append(
						"INSERT INTO PA_MEASURE (PA_MEASURE_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,MEASURETYPE,MANUALACTUAL,MANUALNOTE,CALCULATIONCLASS,PA_MEASURECALC_ID,PA_BENCHMARK_ID,PA_RATIO_ID,PA_HIERARCHY_ID,MEASUREDATATYPE,R_REQUESTTYPE_ID,C_PROJECTTYPE_ID) values (103,11,0,'Y',to_date('20.01.06','DD.MM.RR'),100,to_date('20.01.06','DD.MM.RR'),100,'Service Requests (Time)',null,'Q',0,null,null,null,null,null,null,'T',101,null);\n")
				.append(
						"INSERT INTO PA_MEASURE (PA_MEASURE_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,MEASURETYPE,MANUALACTUAL,MANUALNOTE,CALCULATIONCLASS,PA_MEASURECALC_ID,PA_BENCHMARK_ID,PA_RATIO_ID,PA_HIERARCHY_ID,MEASUREDATATYPE,R_REQUESTTYPE_ID,C_PROJECTTYPE_ID) values (104,11,0,'Y',to_date('20.01.06','DD.MM.RR'),100,to_date('20.01.06','DD.MM.RR'),100,'Service Requests (Status)',null,'Q',0,null,null,null,null,null,null,'S',101,null);\n")
				.append(
						"INSERT INTO PA_MEASURE (PA_MEASURE_ID,AD_CLIENT_ID,AD_ORG_ID,ISACTIVE,CREATED,CREATEDBY,UPDATED,UPDATEDBY,NAME,DESCRIPTION,MEASURETYPE,MANUALACTUAL,MANUALNOTE,CALCULATIONCLASS,PA_MEASURECALC_ID,PA_BENCHMARK_ID,PA_RATIO_ID,PA_HIERARCHY_ID,MEASUREDATATYPE,R_REQUESTTYPE_ID,C_PROJECTTYPE_ID) values (101,11,0,'Y',to_date('25.12.05','DD.MM.RR'),100,to_date('26.12.05','DD.MM.RR'),101,'Invoices Gross Revenue',null,'C',0,null,null,101,null,null,null,'T',null,null);\n")
				.append("\n");

		return buffer.toString();
	}

	/**
	 * Returns the update statement to set the db version to the one needed for
	 * 253b. You need to change this if the target db is not 253b.
	 * 
	 * @return
	 */
	private String getUpdateVersionStatement() {
		return "UPDATE AD_SYSTEM SET VERSION='2006-01-20';";
	}

	/**
	 * Erzeugt eine Connection zur Auftrags-Datenbank.
	 * 
	 * @return Connection
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	private Connection getConnection(String dbName, String username, String password, String url)
			throws ClassNotFoundException, SQLException {

		Connection connection;
		// Load the JDBC driver
		String driverName = "oracle.jdbc.OracleDriver";
		Class.forName(driverName);

		url = url + dbName;

		connection = DriverManager.getConnection(url, username, password);
		return connection;
	}

	/**
	 * Comparator for insert statements. Sorts AD_* entries.
	 * 
	 * @author Karsten Thiemann, kt@schaeffer-ag.de
	 * @deprecated
	 */
	private class AD_Comparator implements Comparator {

		String[] reihenfolge = new String[] { "AD_ELEMENT(", "AD_WINDOW(", "AD_WINDOW_ACCESS(",
				"AD_TABLE(", "AD_REFERENCE(", "AD_VAL_RULE(", "AD_REPORTVIEW(", "AD_PROCESS(",
				"AD_PROCESS_ACCESS(", "AD_PROCESS_PARA(", "AD_PINSTANCE(", "AD_PINSTANCE_PARA(",
				"AD_COLUMN(", "AD_TAB(", "AD_FIELD(", "AD_FORM(", "AD_FORM_ACCESS(", "AD_MENU(",
				"AD_PRINTFORMAT(", "AD_PRINTFORMATITEM(", "AD_MESSAGE(", "AD_REF_LIST(",
				"AD_TREE(", "AD_TREENODE(", "AD_TREENODEMM(", };

		public int compare(Object arg1, Object arg2) {
			if (!(arg1 instanceof String && arg2 instanceof String)) {
				return 0;
			}
			final String str1 = (String) arg1;
			final String str2 = (String) arg2;
			int prio1 = 100;
			int prio2 = 100;
			for (int i = 0; i < reihenfolge.length; i++) {
				if (str1.startsWith("INSERT INTO " + reihenfolge[i])) {
					prio1 = i;
					break;
				}
			}
			for (int i = 0; i < reihenfolge.length; i++) {
				if (str2.startsWith("INSERT INTO " + reihenfolge[i])) {
					prio2 = i;
					break;
				}
			}
			if (prio1 != prio2) {
				if (prio1 < prio2) {
					return -1;
				} else {
					return 1;
				}
			} else
				return str1.compareTo(str2);

		}

	}
}
