<?php
//
// Created on: <28-Jan-2004 16:10:44 dr>
//
// Copyright (C) 1999-2005 eZ systems as. All rights reserved.
//
// This source file is part of the eZ publish (tm) Open Source Content
// Management System.
//
// This file may be distributed and/or modified under the terms of the
// "GNU General Public License" version 2 as published by the Free
// Software Foundation and appearing in the file LICENSE included in
// the packaging of this file.
//
// Licencees holding a valid "eZ publish professional licence" version 2
// may use this file in accordance with the "eZ publish professional licence"
// version 2 Agreement provided with the Software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE.
//
// The "eZ publish professional licence" version 2 is available at
// http://ez.no/ez_publish/licences/professional/ and in the file
// PROFESSIONAL_LICENCE included in the packaging of this file.
// For pricing of this licence please contact us via e-mail to licence@ez.no.
// Further contact information is available at http://ez.no/company/contact/.
//
// The "GNU General Public License" (GPL) is available at
// http://www.gnu.org/copyleft/gpl.html.
//
// Contact licence@ez.no if any conditions of this licencing isn't clear to
// you.
//

/*!
  \class eZDBSchemaChecker ezdbschemachecker.php
  \ingroup eZDBSchema
  \brief Checks differences between schemas

*/

class eZDbSchemaChecker
{
    /*!
     \static
     Finds the difference between the scemas \a $schema1 and \a $schema2.
     \return An array containing:
             - new_tables - A list of new tables that have been added
             - removed_tables - A list of tables that have been removed
             - table_changes - Changes in table definition
               - added_fields - A list of new fields in the table
               - removed_fields - A list of removed fields in the table
               - changed_fields - A list of fields that have changed definition
               - added_indexes - A list of new indexes in the table
               - removed_indexes - A list of removed indexes in the table
               - changed_indexes - A list of indexes that have changed definition
    */
	function diff( $schema1, $schema2 = array(), $schema1Type = false, $schema2Type = false )
	{
		if ( !is_array( $schema1 ) )
		{
			return false;
		}
		$diff = array();

		foreach ( $schema2 as $name => $def )
        {
            // Skip the info structure, this is not a table
            if ( $name == '_info' )
                continue;

			if ( !isset( $schema1[$name] ) )
            {
				$diff['new_tables'][$name] = $def;
			}
            else
            {
				$table_diff = eZDbSchemaChecker::diffTable( $schema1[$name], $def, $schema1Type, $schema2Type );
				if ( count( $table_diff ) )
                {
					$diff['table_changes'][$name] = $table_diff;
				}
			}
		}

		/* Check if there are tables removed */
		foreach ( $schema1 as $name => $def )
        {
            // Skip the info structure, this is not a table
            if ( $name == '_info' )
                continue;

			if ( !isset( $schema2[$name] ) )
            {
				$diff['removed_tables'][$name] = $def;
			}
			else if ( isset( $schema2[$name]['removed'] ) and
                      isset( $schema2[$name]['removed'] ) )
            {
				$diff['removed_tables'][$name] = $def;
			}
		}

		return $diff;
	}

    /*!
     \static
     Finds the difference between the tables \a $table1 and \a $table2 by looking
     at the fields and indexes.

     \return An array containing:
             - added_fields - A list of new fields in the table
             - removed_fields - A list of removed fields in the table
             - changed_fields - A list of fields that have changed definition
             - added_indexes - A list of new indexes in the table
             - removed_indexes - A list of removed indexes in the table
             - changed_indexes - A list of indexes that have changed definition
    */
	function diffTable( $table1, $table2, $schema1Type, $schema2Type )
	{
		$table_diff = array();

		/* See if all the fields in table 1 exist in table 2 */
		foreach ( $table2['fields'] as $name => $def )
        {
			if ( !isset( $table1['fields'][$name] ) )
            {
				$table_diff['added_fields'][$name] = $def;
			}
		}
		/* See if there are any removed fields in table 2 */
		foreach ( $table1['fields'] as $name => $def )
        {
			if ( !isset( $table2['fields'][$name] ) )
            {
				$table_diff['removed_fields'][$name] = true;
			}
			else if ( isset( $table2['fields'][$name]['removed'] ) and
                      $table2['fields'][$name]['removed'] )
            {
				$table_diff['removed_fields'][$name] = true;
			}
		}
		/* See if there are any changed definitions */
		foreach ( $table1['fields'] as $name => $def )
        {
			if ( isset( $table2['fields'][$name] ) )
            {
				if ( is_array( $field_diff = eZDbSchemaChecker::diffField( $def, $table2['fields'][$name], $schema1Type, $schema2Type ) ) )
                {
					$table_diff['changed_fields'][$name] = $field_diff;
				}
			}
		}

        $table1Indexes = $table1['indexes'];
        $table2Indexes = $table2['indexes'];

		/* See if all the indexes in table 1 exist in table 2 */
        foreach ( $table2Indexes as $name => $def )
        {
            if ( !isset( $table1Indexes[$name] ) )
            {
                $table_diff['added_indexes'][$name] = $def;
            }
        }
		/* See if there are any removed indexes in table 2 */
		foreach ( $table1Indexes as $name => $def )
        {
			if ( !isset( $table2Indexes[$name] ) )
            {
				$table_diff['removed_indexes'][$name] = $def;
			}
			else if ( isset( $table2Indexes[$name]['removed'] ) and
                      $table2Indexes[$name]['removed'] )
            {
                if ( isset( $table2Indexes[$name]['comments'] ) )
                    $def['comments'] = array_merge( isset( $def['comments'] ) ? $def['comments'] : array(),
                                                    $table2Indexes[$name]['comments'] );
				$table_diff['removed_indexes'][$name] = $def;
			}
		}
		/* See if there are any changed definitions */
		foreach ( $table1Indexes as $name => $def )
        {
			if ( isset( $table2Indexes[$name] ) )
            {
				if ( is_array( $index_diff = eZDbSchemaChecker::diffIndex( $def, $table2Indexes[$name], $schema1Type, $schema2Type ) ) )
                {
					$table_diff['changed_indexes'][$name] = $index_diff;
				}
			}
		}

		return $table_diff;
	}

    /*!
     \static
     Finds the difference between the field \a $field1 and \a $field2.

     \return The field definition of the changed field or \c false if there are no changes.
    */
	function diffField( $field1, $field2, $schema1Type, $schema2Type )
	{
		/* Type is always available */
        if ( $field1['type'] != $field2['type'] )
        {
            return array( 'different-options' => array( 'type' ), 'field-def' => $field2 );
            return $field2;
        }
		
        $test_fields = array( 'length', 'default', 'not_null' );
        $different_options = array();

        foreach ( $test_fields as $test_field )
        {
			if ( isset( $field1[$test_field] ) )
            {
				if ( !isset( $field2[$test_field] ) ||
                     ( $field1[$test_field] != $field2[$test_field] ) )
                {
                    $different_options[] = $test_field;
				}
			}
            else
            {
				if ( isset( $field2[$test_field] ) )
                {
                    $different_options[] = $test_field;
				}
			}
        }

        if ( $different_options )
            return array( 'different-options' => $different_options, 'field-def' => $field2 );
        else
            return false;
	}

    /*!
     \static
     Finds the difference between the indexes \a $index1 and \a $index2.

     \return The index definition of the changed index or \c false if there are no changes.
    */
	function diffIndex( $index1, $index2, $schema1Type, $schema2Type )
	{
		if ( ( $index1['type'] != $index2['type'] ) ||
             count( array_diff( $index1, $index2 ) ) )
		{
			return $index2;
		}

		$test_fields = array( 'link_table' );
		foreach ( $test_fields as $test_field )
        {
			if ( isset($index1[$test_field] ) )
            {
				if ( !isset( $index2[$test_field] ) ||
                     ( $index1[$test_field] != $index2[$test_field] ) )
                {
					return $index2;
				}
			}
            else
            {
				if ( isset($index2[$test_field] ) )
                {
					return $index2;
				}
			}
		}

		$test_fields = array( 'fields', 'link_fields' );
		foreach ( $test_fields as $test_field )
        {
			if ( isset( $index1[$test_field] ) )
            {
				if ( !isset( $index2[$test_field] ) ||
                     !( $index1[$test_field] == $index2[$test_field] ) )
                {
					return $index2;
				}
			}
            else
            {
				if ( isset( $index2[$test_field] ) )
                {
					return $index2;
				}
			}
		}
	}
}
?>
