<?php // $Id: XMLDBStatement.class.php,v 1.7 2007/10/10 05:25:14 nicolasconnault Exp $

///////////////////////////////////////////////////////////////////////////
//                                                                       //
// NOTICE OF COPYRIGHT                                                   //
//                                                                       //
// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
//          http://moodle.com                                            //
//                                                                       //
// Copyright (C) 1999 onwards Martin Dougiamas        http://dougiamas.com  //
//           (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com  //
//                                                                       //
// This program is free software; you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by  //
// the Free Software Foundation; either version 2 of the License, or     //
// (at your option) any later version.                                   //
//                                                                       //
// This program 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 General Public License for more details:                          //
//                                                                       //
//          http://www.gnu.org/copyleft/gpl.html                         //
//                                                                       //
///////////////////////////////////////////////////////////////////////////

/// This class represent one XMLDB Statement
/// (a group of SQL arbitrary sentences)
/// (only INSERT is allowed for now)

class XMLDBStatement extends XMLDBObject {

    var $table;     // Table we are handling
    var $type;      // XMLDB_STATEMENT_TYPE
    var $sentences; // Collection of sentences in the statement

    /**
     * Creates one new XMLDBStatement
     */
    function XMLDBStatement($name) {
        parent::XMLDBObject($name);
        $this->table     = NULL;
        $this->type      = XMLDB_STATEMENT_INCORRECT;
        $this->sentences = array();
    }

    /**
     * Get the statement table
     */
    function getTable() {
        return $this->table;
    }

    /**
     * Get the statement type
     */
    function getType() {
        return $this->type;
    }

    /**
     * Get the statement sentences
     */
    function &getSentences() {
        return $this->sentences;
    }

    /**
     * Set the statement table
     */
    function setTable($table) {
        $this->table = $table;
    }

    /**
     * Set the statement type
     */
    function setType($type) {
        $this->type = $type;
    }

    /**
     * Add one statement sentence
     */
    function addSentence($sentence) {
        $this->sentences[] = $sentence;
    }

    /**
     * Load data from XML to the index
     */
    function arr2XMLDBStatement($xmlarr) {

        $result = true;

    /// Debug the table
    /// traverse_xmlize($xmlarr);                   //Debug
    /// print_object ($GLOBALS['traverse_array']);  //Debug
    /// $GLOBALS['traverse_array']="";              //Debug

    /// Process key attributes (table, type, comment, previous, next)
        if (isset($xmlarr['@']['TABLE'])) {
            $this->table = strtolower(trim($xmlarr['@']['TABLE']));
        } else {
            $this->errormsg = 'Missing TABLE attribute';
            $this->debug($this->errormsg);
            $result = false;
        }

        if (isset($xmlarr['@']['TYPE'])) {
        /// Check for valid type
            $type = $this->getXMLDBStatementType(trim($xmlarr['@']['TYPE']));
            if ($type) {
                $this->type = $type;
            } else {
                $this->errormsg = 'Invalid TYPE attribute';
                $this->debug($this->errormsg);
                $result = false;
            }
        } else {
            $this->errormsg = 'Missing TYPE attribute';
            $this->debug($this->errormsg);
            $result = false;
        }

    /// Look for sentences
        $sentencesarr = array();

        if (isset($xmlarr['#']['SENTENCES'])) {
            $sentences = $xmlarr['#']['SENTENCES'][0]['#']['SENTENCE'];
            if ($sentences) {
                foreach ($sentences as $sentence) {
                    if (isset($sentence['@']['TEXT'])) {
                        $sentencesarr[] = trim($sentence['@']['TEXT']);
                    } else {
                        $this->errormsg = 'Missing TEXT attribute in sentence';
                        $this->debug($this->errormsg);
                        $result = false;
                    }
                }
            }
        }

    /// Finally, set the array of sentences
        $this->sentences = $sentencesarr;

    /// Now, perform some validations over sentences
    /// XMLDB_STATEMENT_INSERT checks
        if ($this->type == XMLDB_STATEMENT_INSERT) {
        /// Separate fields and values into two arrays
            if ($this->sentences) {
                foreach ($this->sentences as $sentence) {
                    $fields = $this->getFieldsFromInsertSentence($sentence);
                    $values = $this->getValuesFromInsertSentence($sentence);
                /// Check that we aren't inserting the id field
                    if (in_array('id', $fields)) {
                        $this->errormsg = 'Cannot insert the "id" field. It is an autonumeric column';
                        $this->debug($this->errormsg);
                        $result = false;
                    }
                    if ($result && count($fields) == 0) {
                        $this->errormsg = 'Missing fields in sentence "' . $sentence . '"';
                        $this->debug($this->errormsg);
                        $result = false;
                    }
                    if ($result && count($values) == 0) {
                        $this->errormsg = 'Missing values in sentence "' . $sentence . '"';
                        $this->debug($this->errormsg);
                        $result = false;
                    }
                    if ($result && count($fields) != count($values)) {
                        $this->errormsg = 'Incorrect number of fields (' .implode(', ', $fields) . ') or values (' . implode(', ', $values) . ')';
                        $this->debug($this->errormsg);
                        $result = false;
                    }
                }
            }
        } else {
        /// Sentences different from INSERT are not valid for now
            $this->errormsg = 'Only INSERT statements are supported';
            $this->debug($this->errormsg);
            $result = false;
        }

        if (isset($xmlarr['@']['COMMENT'])) {
            $this->comment = trim($xmlarr['@']['COMMENT']);
        }

        if (isset($xmlarr['@']['PREVIOUS'])) {
            $this->previous = trim($xmlarr['@']['PREVIOUS']);
        }

        if (isset($xmlarr['@']['NEXT'])) {
            $this->next = trim($xmlarr['@']['NEXT']);
        }

    /// Set some attributes
        if ($result) {
            $this->loaded = true;
        }
        $this->calculateHash();
        return $result;
    }

    /**
     * This function returns the correct XMLDB_STATEMENT_XXX value for the
     * string passed as argument
     */
    function getXMLDBStatementType($type) {

        $result = XMLDB_STATEMENT_INCORRECT;

        switch (strtolower($type)) {
            case 'insert':
                $result = XMLDB_STATEMENT_INSERT;
                break;
            case 'update':
                $result = XMLDB_STATEMENT_UPDATE;
                break;
            case 'delete':
                $result = XMLDB_STATEMENT_DELETE;
                break;
            case 'custom':
                $result = XMLDB_STATEMENT_CUSTOM;
                break;
        }
    /// Return the normalized XMLDB_STATEMENT
        return $result;
    }

    /**
     * This function returns the correct name value for the
     * XMLDB_STATEMENT_XXX passed as argument
     */
    function getXMLDBStatementName($type) {

        $result = '';

        switch (strtolower($type)) {
            case XMLDB_STATEMENT_INSERT:
                $result = 'insert';
                break;
            case XMLDB_STATEMENT_UPDATE:
                $result = 'update';
                break;
            case XMLDB_STATEMENT_DELETE:
                $result = 'delete';
                break;
            case XMLDB_STATEMENT_CUSTOM:
                $result = 'custom';
                break;
        }
    /// Return the normalized name
        return $result;
    }

    /**
     * This function calculate and set the hash of one XMLDBStatement
     */
     function calculateHash($recursive = false) {
        if (!$this->loaded) {
            $this->hash = NULL;
        } else {
            $key = $this->table . $this->type . implode (', ', $this->sentences);
            $this->hash = md5($key);
        }
    }

    /**
     * This function will output the XML text for one statement
     */
    function xmlOutput() {
        $o = '';
        $o.= '    <STATEMENT NAME="' . $this->name . '" TYPE="' . XMLDBStatement::getXMLDBStatementName($this->type) . '" TABLE="' . $this->table . '"';
        if ($this->comment) {
            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
        }
        if ($this->previous) {
            $o.= ' PREVIOUS="' . $this->previous . '"';
        }
        if ($this->next) {
            $o.= ' NEXT="' . $this->next . '"';
        }
        if ($this->sentences) {
            $o.= '>' . "\n";
            $o.= '      <SENTENCES>' . "\n";
            foreach ($this->sentences as $sentence) {
                $o.= '        <SENTENCE TEXT="' . htmlspecialchars($sentence) . '" />' . "\n";
            }
            $o.= '      </SENTENCES>' . "\n";
            $o.= '    </STATEMENT>' . "\n";
        } else {
            $o.= '/>' . "\n";
        }

        return $o;
    }

    /**
     * This function will set all the attributes of the XMLDBIndex object
     * based on information passed in one ADOindex
     */
    function setFromADOIndex($adoindex) {

    /// Set the unique field
        $this->unique = false;
    /// Set the fields
        $this->fields = $adoindex['columns'];
    /// Some more fields
        $this->loaded = true;
        $this->changed = true;
    }

    /**
     * Shows info in a readable format
     */
    function readableInfo() {
        $o = '';
    /// unique
        if ($this->unique) {
            $o .= 'unique';
        } else {
            $o .= 'not unique';
        }
    /// fields
        $o .= ' (' . implode(', ', $this->fields) . ')';

        return $o;
    }

    /**
     * This function will return an array of fields from one INSERT sentence
     */
    function getFieldsFromInsertSentence($sentence) {

        $fields = array();

    /// Get first part from the sentence (before VALUES)
        preg_match('/^\((.*)\)\s+VALUES/is', $sentence, $matches);
        if (isset($matches[1])) {
            $part = $matches[1];
        /// Convert the comma separated string to an array
            $arr = $this->comma2array($part);
            if ($arr) {
                $fields = $arr;
            }
        }

        return $fields;
    }

    /**
     * This function will return an array of values from one INSERT sentence
     */
    function getValuesFromInsertSentence($sentence) {

        $values = array();

    /// Get second part from the sentence (after VALUES)
        preg_match('/VALUES\s*\((.*)\)$/is', $sentence, $matches);
        if (isset($matches[1])) {
            $part = $matches[1];
        /// Convert the comma separated string to an array
            $arr = $this->comma2array($part);
            if ($arr) {
                $values = $arr;
            }
        }

        return $values;
    }

    /**
     * This function will return the code needed to execute a collection
     * of sentences present inside one statement for the specified BD
     * and prefix.
     * For now it only supports INSERT statements
     */
    function getExecuteStatementSQL ($dbtype, $prefix, $statement_end=true) {

        $results = array();

    /// Based on statement type
        switch ($this->type) {
            case XMLDB_STATEMENT_INSERT:
                $results = $this->getExecuteInsertSQL($dbtype, $prefix, $statement_end);
                break;
            case XMLDB_STATEMENT_UPDATE:
                break;
            case XMLDB_STATEMENT_DELETE:
                break;
            case XMLDB_STATEMENT_CUSTOM:
                break;
        }

        return $results;
    }

    /**
     * This function will return the code needed to execute a collection
     * of insert sentences present inside the statement for the specified BD
     * and prefix. Just one simple wrapper over generators.
     */
    function getExecuteInsertSQL ($dbtype, $prefix, $statement_end=true) {

        $results = array();

        $classname = 'XMLDB' . $dbtype;
        $generator = new $classname();
        $generator->setPrefix($prefix);

        $results = $generator->getExecuteInsertSQL($this);

        if ($statement_end) {
            $results = $generator->getEndedStatements($results);
        }

        return $results;
    }

}

?>
