]> git.mjollnir.org Git - moodle.git/commitdiff
Initial commit on HEAD of all the XMLDB stuff.
authorstronk7 <stronk7>
Tue, 15 Aug 2006 09:14:31 +0000 (09:14 +0000)
committerstronk7 <stronk7>
Tue, 15 Aug 2006 09:14:31 +0000 (09:14 +0000)
12 files changed:
lib/xmldb/classes/XMLDBConstants.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBField.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBFile.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBIndex.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBKey.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBObject.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBStatement.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBStructure.class.php [new file with mode: 0644]
lib/xmldb/classes/XMLDBTable.class.php [new file with mode: 0644]
lib/xmldb/classes/generators/mysql/mysql.class.php [new file with mode: 0644]
lib/xmldb/xmldb.dtd [new file with mode: 0644]
lib/xmldb/xmldb.xsd [new file with mode: 0644]

diff --git a/lib/xmldb/classes/XMLDBConstants.php b/lib/xmldb/classes/XMLDBConstants.php
new file mode 100644 (file)
index 0000000..89bb098
--- /dev/null
@@ -0,0 +1,69 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 file contains all the constants and variables used 
+/// by the XMLDB interface
+
+/// First, some constants to be used by actions
+    define('ACTION_NONE',             0);  //Default flags for class
+    define('ACTION_GENERATE_HTML',    1);  //The invoke function will return HTML
+    define('ACTION_GENERATE_XML',     2);  //The invoke function will return HTML
+    define('ACTION_HAVE_SUBACTIONS',  1);  //The class can have subaction
+
+/// Now the allowed DB Field Types
+    define ('XMLDB_TYPE_INCORRECT',   0);  //Wrong DB Type
+    define ('XMLDB_TYPE_INTEGER',     1);  //Integer 
+    define ('XMLDB_TYPE_NUMBER',      2);  //Decimal number 
+    define ('XMLDB_TYPE_FLOAT',       3);  //Floating Point number 
+    define ('XMLDB_TYPE_CHAR',        4);  //String
+    define ('XMLDB_TYPE_TEXT',        5);  //Text
+    define ('XMLDB_TYPE_BINARY',      6);  //Binary
+    define ('XMLDB_TYPE_DATETIME',    7);  //Datetime
+    define ('XMLDB_TYPE_TIMESTAMP',   8);  //Timestamp
+
+/// Now the allowed DB Keys
+    define ('XMLDB_KEY_INCORRECT',     0);  //Wrong DB Key
+    define ('XMLDB_KEY_PRIMARY',       1);  //Primary Keys 
+    define ('XMLDB_KEY_UNIQUE',        2);  //Unique Keys
+    define ('XMLDB_KEY_FOREIGN',       3);  //Foreign Keys 
+    define ('XMLDB_KEY_CHECK',         4);  //Check Constraints - NOT USED!
+    define ('XMLDB_KEY_FOREIGN_UNIQUE',5);  //Foreign Key + Unique Key
+
+/// Now the allowed Statement Types
+    define ('XMLDB_STATEMENT_INCORRECT',   0);  //Wrong Statement Type
+    define ('XMLDB_STATEMENT_INSERT',      1);  //Insert Statements
+    define ('XMLDB_STATEMENT_UPDATE',      2);  //Update Statements
+    define ('XMLDB_STATEMENT_DELETE',      3);  //Delete Statements
+    define ('XMLDB_STATEMENT_CUSTOM',      4);  //Custom Statements
+
+/// Some other useful Constants
+    define ('XMLDB_UNSIGNED',        true);  //If the field is going to be unsigned
+    define ('XMLDB_NOTNULL',         true);  //If the field is going to be not null
+    define ('XMLDB_SEQUENCE',        true);  //If the field is going to be a sequence
+    define ('XMLDB_ENUM',            true);  //If the field is going to be a enumeration of possible fields
+    define ('XMLDB_INDEX_UNIQUE',    true);  //If the index is going to be unique
+    define ('XMLDB_INDEX_NOTUNIQUE',false);  //If the index is NOT going to be unique
+?>
diff --git a/lib/xmldb/classes/XMLDBField.class.php b/lib/xmldb/classes/XMLDBField.class.php
new file mode 100644 (file)
index 0000000..1f484d7
--- /dev/null
@@ -0,0 +1,733 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 Field
+
+class XMLDBField extends XMLDBObject {
+    var $type;
+    var $length;
+    var $unsigned;
+    var $notnull;
+    var $default;
+    var $sequence;
+    var $enum;
+    var $enumvalues;
+    var $decimals;
+
+    /**
+     * Creates one new XMLDBField
+     */
+    function XMLDBField($name) {
+        parent::XMLDBObject($name);
+        $this->type = NULL;
+        $this->length = NULL;
+        $this->unsigned = false;
+        $this->notnull = false;
+        $this->default = NULL;
+        $this->sequence = false;
+        $this->enum = false;
+        $this->enumvalues = NULL;
+        $this->decimals = NULL;
+    }
+
+    /**
+     * Get the type
+     */
+    function getType() {
+        return $this->type;
+    }
+
+    /**
+     * Get the length
+     */
+    function getLength() {
+        return $this->length;
+    }
+
+    /**
+     * Get the decimals
+     */
+    function getDecimals() {
+        return $this->decimals;
+    }
+
+    /**
+     * Get the notnull
+     */
+    function getNotNull() {
+        return $this->notnull;
+    }
+
+    /**
+     * Get the unsigned
+     */
+    function getUnsigned() {
+        return $this->unsigned;
+    }
+
+    /**
+     * Get the sequence
+     */
+    function getSequence() {
+        return $this->sequence;
+    }
+
+    /**
+     * Get the enum
+     */
+    function getEnum() {
+        return $this->enum;
+    }
+
+    /**
+     * Get the enumvalues
+     */
+    function getEnumValues() {
+        return $this->enumvalues;
+    }
+
+    /**
+     * Get the default
+     */
+    function getDefault() {
+        return $this->default;
+    }
+
+    /**
+     * Set the field type
+     */
+    function setType($type) {
+        $this->type = $type;
+    }
+
+    /**
+     * Set the field length
+     */
+    function setLength($length) {
+        $this->length = $length;
+    }
+
+    /**
+     * Set the field decimals
+     */
+    function setDecimals($decimals) {
+        $this->decimals = $decimals;
+    }
+
+    /**
+     * Set the field unsigned
+     */
+    function setUnsigned($unsigned=true) {
+        $this->unsigned = $unsigned;
+    }
+
+    /**
+     * Set the field notnull
+     */
+    function setNotNull($notnull=true) {
+        $this->notnull = $notnull;
+    }
+
+    /**
+     * Set the field sequence
+     */
+    function setSequence($sequence=true) {
+        $this->sequence = $sequence;
+    }
+
+    /**
+     * Set the field enum
+     */
+    function setEnum($enum=true) {
+        $this->enum = $enum;
+    }
+
+    /**
+     * Set the field enumvalues
+     */
+    function setEnumValues($enumvalues) {
+        $this->enumvalues = $enumvalues;
+    }
+
+    /**
+     * Set the field default
+     */
+    function setDefault($default) {
+        $this->default = $default;
+    }
+
+    /**
+     * Load data from XML to the table
+     */
+    function arr2XMLDBField($xmlarr) {
+
+        $result = true;
+
+    /// Debug the table
+    /// traverse_xmlize($xmlarr);                   //Debug
+    /// print_object ($GLOBALS['traverse_array']);  //Debug
+    /// $GLOBALS['traverse_array']="";              //Debug
+
+    /// Process table attributes (name, type, length, unsigned,
+    /// notnull, sequence, enum, enumvalues, decimals, comment,
+    /// previous, next)
+        if (isset($xmlarr['@']['NAME'])) {
+            $this->name = trim($xmlarr['@']['NAME']);
+        } else {
+            $this->errormsg = 'Missing NAME attribute';
+            $result = false;
+        }
+
+        if (isset($xmlarr['@']['TYPE'])) {
+        /// Check for valid type
+            $type = $this->getXMLDBFieldType(trim($xmlarr['@']['TYPE']));
+            if ($type) {
+                $this->type = $type;
+            } else {
+                $this->errormsg = 'Invalid TYPE attribute';
+                $result = false;
+            }
+        } else {
+            $this->errormsg = 'Missing TYPE attribute';
+            $result = false;
+        }
+
+        if (isset($xmlarr['@']['LENGTH'])) {
+            $length = trim($xmlarr['@']['LENGTH']);
+        /// Check for integer values
+            if ($this->type == XMLDB_TYPE_INTEGER ||
+                $this->type == XMLDB_TYPE_NUMBER ||
+                $this->type == XMLDB_TYPE_CHAR) {
+                if (!(is_numeric($length)&&(intval($length)==floatval($length)))) {
+                    $this->errormsg = 'Incorrect LENGTH attribute for int, number or char fields';
+                    $result = false;
+                } else if (!$length) {
+                    $this->errormsg = 'Zero LENGTH attribute';
+                    $result = false;
+                }
+            }
+        /// Check for big, medium, small to be applied to text and binary
+            if ($this->type == XMLDB_TYPE_TEXT ||
+                $this->type == XMLDB_TYPE_BINARY) {
+                if (!$length) {
+                    $length == 'big';
+                }
+                if ($length != 'big' &&
+                    $length != 'medium' &&
+                    $length != 'small') {
+                    $this->errormsg = 'Incorrect LENGTH attribute for text and binary fields (only big, medium and small allowed)';
+                    $result = false;
+                }
+            }
+        /// Finally, set the length
+            $this->length = $length;
+        }
+
+        if (isset($xmlarr['@']['UNSIGNED'])) {
+            $unsigned = strtolower(trim($xmlarr['@']['UNSIGNED']));
+            if ($unsigned == 'true') {
+                $this->unsigned = true;
+            } else if ($unsigned == 'false') {
+                $this->unsigned = false;
+            } else {
+                $this->errormsg = 'Incorrect UNSIGNED attribute (true/false allowed)';
+                $result = false;
+            }
+        }
+
+        if (isset($xmlarr['@']['NOTNULL'])) {
+            $notnull = strtolower(trim($xmlarr['@']['NOTNULL']));
+            if ($notnull == 'true') {
+                $this->notnull = true;
+            } else if ($notnull == 'false') {
+                $this->notnull = false;
+            } else {
+                $this->errormsg = 'Incorrect NOTNULL attribute (true/false allowed)';
+                $result = false;
+            }
+        }
+
+        if (isset($xmlarr['@']['SEQUENCE'])) {
+            $sequence = strtolower(trim($xmlarr['@']['SEQUENCE']));
+            if ($sequence == 'true') {
+                $this->sequence = true;
+            } else if ($sequence == 'false') {
+                $this->sequence = false;
+            } else {
+                $this->errormsg = 'Incorrect SEQUENCE attribute (true/false allowed)';
+                $result = false;
+            }
+        }
+
+        if (isset($xmlarr['@']['DEFAULT'])) {
+            $this->default = trim($xmlarr['@']['DEFAULT']);
+        }
+
+        if (isset($xmlarr['@']['ENUM'])) {
+            $enum = strtolower(trim($xmlarr['@']['ENUM']));
+            if ($enum == 'true') {
+                $this->enum = true;
+            } else if ($enum == 'false') {
+                $this->enum = false;
+            } else {
+                $this->errormsg = 'Incorrect ENUM attribute (true/false allowed)';
+                $result = false;
+            }
+        }
+
+        if (isset($xmlarr['@']['ENUMVALUES'])) {
+            $enumvalues = strtolower(trim($xmlarr['@']['ENUMVALUES']));
+            if (!$this->enum) {
+                $this->errormsg = 'Wrong ENUMVALUES attribute (not ENUM)';
+                $result = false;
+                $this->enumvalues = $enumvalues;
+            } else {
+            /// Check we have a valid list (comma separated of quoted values)
+                $enumarr = explode(',',$enumvalues);
+                if ($enumarr) {
+                    foreach ($enumarr as $key => $enumelement) {
+                    /// Clear some spaces
+                        $enumarr[$key] = trim($enumelement);
+                        $enumelement = trim($enumelement);
+                    /// Skip if under error
+                        if (!$result) {
+                            continue;
+                        }
+                    /// Look for quoted strings
+                        if (substr($enumelement, 0, 1) != "'" ||
+                            substr($enumelement, -1, 1) != "'") {
+                            $this->errormsg = 'Incorrect ENUMVALUES attribute (some value is not properly quoted)';
+                            $result = false;
+                        }
+                    }
+                } else {
+                    $this->errormsg = 'Incorrect ENUMVALUES attribute (comma separated of quoted values)';
+                    $result = false;
+                }
+            }
+        } else if ($this->enum) {
+            $this->errormsg = 'Incorrect ENUMVALUES attribute (field is not declared as ENUM)';
+            $result = false;
+        }
+    /// Finally, set the value
+        if ($this->enum) {
+            $this->enumvalues = $enumarr;
+        }
+
+        $decimals = NULL;
+        if (isset($xmlarr['@']['DECIMALS'])) {
+            $decimals = trim($xmlarr['@']['DECIMALS']);
+        /// Check for integer values
+            if ($this->type == XMLDB_TYPE_NUMBER || 
+                $this->type == XMLDB_TYPE_FLOAT) {
+                if (!(is_numeric($decimals)&&(intval($decimals)==floatval($decimals)))) {
+                    $this->errormsg = 'Incorrect DECIMALS attribute for number field';
+                    $result = false;
+                } else if ($this->length <= $decimals){
+                    $this->errormsg = 'Incorrect DECIMALS attribute (bigget than length)';
+                    $result = false;
+                }
+            } else {
+                $this->errormsg = 'Incorrect DECIMALS attribute for non-number field';
+                $result = false;
+            }
+        } else {
+            if ($this->type == XMLDB_TYPE_NUMBER) {
+                $decimals = 0;
+            }
+        }
+     // Finally, set the decimals
+        if ($this->type == XMLDB_TYPE_NUMBER ||
+            $this->type == XMLDB_TYPE_FLOAT) {
+            $this->decimals = $decimals;
+        }
+
+        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_TYPE_XXX value for the
+     * string passed as argument
+     */
+    function getXMLDBFieldType($type) {
+
+        $result = XMLDB_TYPE_INCORRECT;
+        
+        switch (strtolower($type)) {
+            case 'int':
+                $result = XMLDB_TYPE_INTEGER;
+                break;
+            case 'number':
+                $result = XMLDB_TYPE_NUMBER;
+                break;
+            case 'float':
+                $result = XMLDB_TYPE_FLOAT;
+                break;
+            case 'char':
+                $result = XMLDB_TYPE_CHAR;
+                break;
+            case 'text':
+                $result = XMLDB_TYPE_TEXT;
+                break;
+            case 'binary':
+                $result = XMLDB_TYPE_BINARY;
+                break;
+            case 'datetime':
+                $result = XMLDB_TYPE_DATETIME;
+                break;
+        }
+    /// Return the normalized XMLDB_TYPE
+        return $result;
+    }
+
+    /**
+     * This function returns the correct name value for the
+     * XMLDB_TYPE_XXX passed as argument
+     */
+    function getXMLDBTypeName($type) {
+
+        $result = "";
+        
+        switch (strtolower($type)) {
+            case XMLDB_TYPE_INTEGER:
+                $result = 'int';
+                break;
+            case XMLDB_TYPE_NUMBER:
+                $result = 'number';
+                break;
+            case XMLDB_TYPE_FLOAT:
+                $result = 'float';
+                break;
+            case XMLDB_TYPE_CHAR:
+                $result = 'char';
+                break;
+            case XMLDB_TYPE_TEXT:
+                $result = 'text';
+                break;
+            case XMLDB_TYPE_BINARY:
+                $result = 'binary';
+                break;
+            case XMLDB_TYPE_DATETIME:
+                $result = 'datetime';
+                break;
+        }
+    /// Return the normalized name
+        return $result;
+    }
+
+    /**
+     * This function calculate and set the hash of one XMLDBField
+     */
+     function calculateHash($recursive = false) {
+        if (!$this->loaded) {
+            $this->hash = NULL;
+        } else {
+            $key = $this->name . $this->type . $this->length .
+                   $this->unsigned . $this->notnull . $this->sequence .
+                   $this->decimals . $this->comment;
+            if ($this->enum) {
+                $key .= implode(', ',$this->enumvalues);
+            }
+            $this->hash = md5($key);
+        }
+    }
+
+    /** 
+     *This function will output the XML text for one field
+     */
+    function xmlOutput() {
+        $o = '';
+        $o.= '        <FIELD NAME="' . $this->name . '"';
+        $o.= ' TYPE="' . $this->getXMLDBTypeName($this->type) . '"';
+        if ($this->length) {
+            $o.= ' LENGTH="' . $this->length . '"';
+        }
+        if ($this->notnull) {
+            $notnull = 'true';
+        } else {
+            $notnull = 'false';
+        }
+        $o.= ' NOTNULL="' . $notnull . '"';
+        if ($this->unsigned) {
+            $unsigned = 'true';
+        } else {
+            $unsigned = 'false';
+        }
+        if ($this->type == XMLDB_TYPE_INTEGER ||
+            $this->type == XMLDB_TYPE_NUMBER ||
+            $this->type == XMLDB_TYPE_FLOAT) {
+            if ($this->unsigned) {
+                $unsigned = 'true';
+            } else {
+                $unsigned = 'false';
+            }
+            $o.= ' UNSIGNED="' . $unsigned . '"';
+        }
+        if (!$this->sequence && $this->default !== NULL) {
+            $o.= ' DEFAULT="' . $this->default . '"';
+        }
+        if ($this->sequence) {
+            $sequence = 'true';
+        } else {
+            $sequence = 'false';
+        }
+        $o.= ' SEQUENCE="' . $sequence . '"';
+        if ($this->enum) {
+            $enum = 'true';
+        } else {
+            $enum = 'false';
+        }
+        $o.= ' ENUM="' . $enum . '"';
+        if ($this->enum) {
+            $o.= ' ENUMVALUES="' . implode(', ', $this->enumvalues) . '"';
+        }
+        if ($this->decimals !== NULL) {
+            $o.= ' DECIMALS="' . $this->decimals . '"';
+        }
+        if ($this->comment) {
+            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+        }
+        if ($this->previous) {
+            $o.= ' PREVIOUS="' . $this->previous . '"';
+        }
+        if ($this->next) {
+            $o.= ' NEXT="' . $this->next . '"';
+        }
+        $o.= '/>' . "\n";
+
+        return $o;
+    }
+
+    /**
+     * This function will set all the attributes of the XMLDBField object
+     * based on information passed in one ADOField
+     */
+    function setFromADOField($adofield) {
+
+    /// Calculate the XMLDB_TYPE
+        switch (strtolower($adofield->type)) {
+            case 'int':
+            case 'tinyint':
+            case 'smallint':
+            case 'bigint':
+            case 'integer':
+                $this->type = XMLDB_TYPE_INTEGER;
+                break;
+            case 'number':
+            case 'decimal':
+            case 'dec':
+            case 'numeric':
+                $this->type = XMLDB_TYPE_NUMBER;
+                break;
+            case 'float':
+            case 'double':
+                $this->type = XMLDB_TYPE_FLOAT;
+                break;
+            case 'char':
+            case 'varchar':
+            case 'enum':
+                $this->type = XMLDB_TYPE_CHAR;
+                break;
+            case 'text':
+            case 'tinytext':
+            case 'mediumtext':
+            case 'longtext':
+                $this->type = XMLDB_TYPE_TEXT;
+                break;
+            case 'blob':
+            case 'tinyblob':
+            case 'mediumblob':
+            case 'longblob':
+                $this->type = XMLDB_TYPE_BINARY;
+                break;
+            case 'datetime':
+            case 'timestamp':
+                $this->type = XMLDB_TYPE_DATETIME;
+                break;
+            default:
+                $this->type = XMLDB_TYPE_TEXT;
+        }
+    /// Calculate the length of the field
+        if ($adofield->max_length > 0 && 
+               ($this->type == XMLDB_TYPE_INTEGER ||
+                $this->type == XMLDB_TYPE_NUMBER  ||
+                $this->type == XMLDB_TYPE_FLOAT   ||
+                $this->type == XMLDB_TYPE_CHAR)) {
+            $this->length = $adofield->max_length;
+        }
+        if ($this->type == XMLDB_TYPE_TEXT) {
+            switch (strtolower($adofield->type)) {
+                case 'tinytext':
+                case 'text':
+                    $this->length = 'small';
+                    break;
+                case 'mediumtext':
+                    $this->length = 'medium';
+                    break;
+                case 'longtext':
+                    $this->length = 'big';
+                    break;
+                default:
+                    $this->length = 'small';
+            }
+        }
+        if ($this->type == XMLDB_TYPE_BINARY) {
+            switch (strtolower($adofield->type)) {
+                case 'tinyblob':
+                case 'blob':
+                    $this->length = 'small';
+                    break;
+                case 'mediumblob':
+                    $this->length = 'medium';
+                    break;
+                case 'longblob':
+                    $this->length = 'big';
+                    break;
+                default:
+                    $this->length = 'small';
+            }
+        }
+    /// Calculate the decimals of the field
+        if ($adofield->max_length > 0 &&
+            $adofield->scale && 
+               ($this->type == XMLDB_TYPE_NUMBER ||
+                $this->type == XMLDB_TYPE_FLOAT)) {
+            $this->decimals = $adofield->scale;
+        }
+    /// Calculate the unsigned field
+        if ($adofield->unsigned && 
+               ($this->type == XMLDB_TYPE_INTEGER ||
+                $this->type == XMLDB_TYPE_NUMBER  ||
+                $this->type == XMLDB_TYPE_FLOAT)) {
+            $this->unsigned = true;
+        }
+    /// Calculate the notnull field
+        if ($adofield->not_null) {
+            $this->notnull = true;
+        }
+    /// Calculate the default field
+        if ($adofield->has_default) {
+            $this->default = $adofield->default_value;
+        } 
+    /// Calculate the sequence field
+        if ($adofield->auto_increment) {
+            $this->sequence = true;
+        /// Sequence fields are always unsigned
+            $this->unsigned = true;
+        }
+    /// Calculate the enum and enumvalues field
+        if ($adofield->type == 'enum') {
+            $this->enum = true;
+            $this->enumvalues = $adofield->enums;
+        }
+    /// Some more fields
+        $this->loaded = true;
+        $this->changed = true;
+    }
+
+    /**
+     * Shows info in a readable format
+     */
+    function readableInfo() {
+        $o = '';
+    /// type
+        $o .= $this->getXMLDBTypeName($this->type);
+    /// length
+        if ($this->type == XMLDB_TYPE_INTEGER ||
+            $this->type == XMLDB_TYPE_NUMBER  ||
+            $this->type == XMLDB_TYPE_FLOAT   ||
+            $this->type == XMLDB_TYPE_CHAR) {
+            if ($this->length) {
+                $o .= ' (' . $this->length;
+                if ($this->type == XMLDB_TYPE_NUMBER  ||
+                    $this->type == XMLDB_TYPE_FLOAT) {
+                    if ($this->decimals !== NULL) {
+                        $o .= ', ' . $this->decimals;
+                    }
+                }
+                $o .= ')';
+            }
+        }
+        if ($this->type == XMLDB_TYPE_TEXT ||
+            $this->type == XMLDB_TYPE_BINARY) {
+                $o .= ' (' . $this->length . ')';
+        }
+    /// enum
+        if ($this->enum) {
+            $o .= ' enum(' . implode(', ', $this->enumvalues) . ')';
+        }
+    /// unsigned
+        if ($this->type == XMLDB_TYPE_INTEGER ||
+            $this->type == XMLDB_TYPE_NUMBER ||
+            $this->type == XMLDB_TYPE_FLOAT) {
+            if ($this->unsigned) {
+                $o .= ' unsigned';
+            } else {
+                $o .= ' signed';
+            }
+        }
+    /// not null
+        if ($this->notnull) {
+            $o .= ' not null';
+        }
+    /// default
+        if ($this->default !== NULL) {
+            $o .= ' default ';
+            if ($this->type == XMLDB_TYPE_CHAR ||
+                $this->type == XMLDB_TYPE_TEXT) {
+                    $o .= "'" . $this->default . "'";
+            } else {
+                $o .= $this->default;
+            }
+        }
+    /// sequence
+        if ($this->sequence) {
+            $o .= ' auto-numbered';
+        }
+
+        return $o;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBFile.class.php b/lib/xmldb/classes/XMLDBFile.class.php
new file mode 100644 (file)
index 0000000..24e4010
--- /dev/null
@@ -0,0 +1,112 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 represents an entire XMLDB file
+
+class XMLDBFile extends XMLDBObject {
+
+    var $path;
+    var $xmldb_structure;
+
+    /**
+     * Constructor of the XMLDBFile
+     */
+    function XMLDBFile ($path) {
+        parent::XMLDBObject($path);
+        $this->path = $path;
+        $this->xmldb_structure = NULL;
+    }
+
+    /**
+     * Determine if the XML file exists
+     */
+    function fileExists() {
+        if (file_exists($this->path) && is_readable($this->path)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Determine if the XML is writeable
+     */
+    function fileWriteable() {
+        if (is_writeable(dirname($this->path))) {
+            return true;
+        }
+        return false;
+    }
+
+    function &getStructure() {
+        return $this->xmldb_structure;
+    }
+
+    /**
+     * Load and the XMLDB structure from file
+     */
+    function loadXMLStructure() {
+        if ($this->fileExists()) {
+        /// File exists, so let's process it
+        /// Load everything to a big array
+            $xmlarr = xmlize(file_get_contents($this->path));
+        /// Convert array to xmldb structure
+            $this->xmldb_structure = $this->arr2XMLDBStructure($xmlarr);
+        /// Analize results
+            if ($this->xmldb_structure->isLoaded()) {
+                $this->loaded = true;
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This function takes an xmlized array and put it into one XMLDBStructure
+     */
+    function arr2XMLDBStructure ($xmlarr) {
+        $structure = new XMLDBStructure($this->path);
+        $structure->arr2XMLDBStructure($xmlarr);
+        return $structure;
+    }
+
+    /** 
+     * This function saves the whole XMLDBStructure to its file
+     */
+    function saveXMLFile() {
+
+        $result = true;
+
+        $structure =& $this->getStructure();
+
+        $result = file_put_contents($this->path, $structure->xmlOutput());
+
+        return $result;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBIndex.class.php b/lib/xmldb/classes/XMLDBIndex.class.php
new file mode 100644 (file)
index 0000000..2fa8947
--- /dev/null
@@ -0,0 +1,221 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 Index
+
+class XMLDBIndex extends XMLDBObject {
+    var $unique;
+    var $fields;
+
+    /**
+     * Creates one new XMLDBIndex
+     */
+    function XMLDBIndex($name) {
+        parent::XMLDBObject($name);
+        $this->unique = false;
+        $this->fields = array();
+    }
+
+     /**
+      * Get the index unique
+      */
+     function getUnique() {
+         return $this->unique;
+     }
+
+     /**
+      * Set the index unique
+      */
+     function setUnique($unique = true) {
+         $this->unique = $unique;
+     }
+
+    /**
+     * Set the index fields
+     */
+    function setFields($fields) {
+        $this->fields = $fields;
+    }
+
+    /** 
+     * Get the index fields
+     */
+    function &getFields() {
+        return $this->fields;
+    }
+
+    /**
+     * Load data from XML to the index
+     */
+    function arr2XMLDBIndex($xmlarr) {
+
+        $result = true;
+
+    /// Debug the table
+    /// traverse_xmlize($xmlarr);                   //Debug
+    /// print_object ($GLOBALS['traverse_array']);  //Debug
+    /// $GLOBALS['traverse_array']="";              //Debug
+
+    /// Process key attributes (name, unique, fields, comment, previous, next)
+        if (isset($xmlarr['@']['NAME'])) {
+            $this->name = trim($xmlarr['@']['NAME']);
+        } else {
+            $this->errormsg = 'Missing NAME attribute';
+            $result = false;
+        }
+
+        if (isset($xmlarr['@']['UNIQUE'])) {
+            $unique = strtolower(trim($xmlarr['@']['UNIQUE']));
+            if ($unique == 'true') {
+                $this->unique = true;
+            } else if ($unique == 'false') {
+                $this->unique = false;
+            } else {
+                $this->errormsg = 'Incorrect UNIQUE attribute (true/false allowed)';
+                $result = false;
+            }
+        } else {
+                $this->errormsg = 'Undefined UNIQUE attribute';
+                $result = false;
+        }
+          
+        if (isset($xmlarr['@']['FIELDS'])) {
+            $fields = strtolower(trim($xmlarr['@']['FIELDS']));
+            if ($fields) {
+                $fieldsarr = explode(',',$fields);
+                if ($fieldsarr) {
+                    foreach ($fieldsarr as $key => $element) {
+                        $fieldsarr [$key] = trim($element);
+                    }
+                } else {
+                    $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
+                    $result = false;
+                }
+            } else {
+                $this->errormsg = 'Empty FIELDS attribute';
+                $result = false;
+            }
+        } else {
+            $this->errormsg = 'Missing FIELDS attribute';
+            $result = false;
+        }
+    /// Finally, set the array of fields
+        $this->fields = $fieldsarr;
+
+        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 calculate and set the hash of one XMLDBIndex
+     */
+     function calculateHash($recursive = false) {
+        if (!$this->loaded) {
+            $this->hash = NULL;
+        } else {
+            $key = $this->unique . implode (', ', $this->fields);
+            $this->hash = md5($key);
+        }
+    }
+
+    /** 
+     *This function will output the XML text for one index
+     */
+    function xmlOutput() {
+        $o = '';
+        $o.= '        <INDEX NAME="' . $this->name . '"';
+        if ($this->unique) {
+            $unique = 'true';
+        } else {
+            $unique = 'false';
+        }
+        $o.= ' UNIQUE="' . $unique . '"';
+        $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
+        if ($this->comment) {
+            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+        }
+        if ($this->previous) {
+            $o.= ' PREVIOUS="' . $this->previous . '"';
+        }
+        if ($this->next) {
+            $o.= ' NEXT="' . $this->next . '"';
+        }
+        $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;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBKey.class.php b/lib/xmldb/classes/XMLDBKey.class.php
new file mode 100644 (file)
index 0000000..61f74d6
--- /dev/null
@@ -0,0 +1,381 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 Key
+
+class XMLDBKey extends XMLDBObject {
+    var $type;
+    var $fields;
+    var $reftable;
+    var $reffields;
+
+    /**
+     * Creates one new XMLDBKey
+     */
+    function XMLDBKey($name) {
+        parent::XMLDBObject($name);
+        $this->type = NULL;
+        $this->fields = array();
+        $this->reftable = NULL;
+        $this->reffields = array();
+    }
+
+    /**
+     * Get the key type
+     */
+    function getType() {
+        return $this->type;
+    }
+
+    /**
+     * Set the key type
+     */
+    function setType($type) {
+        $this->type = $type;
+    }
+
+    /**
+     * Set the key fields
+     */
+    function setFields($fields) {
+        $this->fields = $fields;
+    }
+
+    /**
+     * Set the key reftable
+     */
+    function setRefTable($reftable) {
+        $this->reftable = $reftable;
+    }
+
+    /**
+     * Set the key reffields
+     */
+    function setRefFields($reffields) {
+        $this->reffields = $reffields;
+    }
+
+    /** 
+     * Get the key fields
+     */
+    function &getFields() {
+        return $this->fields;
+    }
+
+    /**
+     * Get the key reftable
+     */
+    function &getRefTable() {
+        return $this->reftable;
+    }
+
+    /**
+     * Get the key reffields
+     */
+    function &getRefFields() {
+        return $this->reffields;
+    }
+
+    /**
+     * Load data from XML to the key
+     */
+    function arr2XMLDBKey($xmlarr) {
+
+        $result = true;
+
+    /// Debug the table
+    /// traverse_xmlize($xmlarr);                   //Debug
+    /// print_object ($GLOBALS['traverse_array']);  //Debug
+    /// $GLOBALS['traverse_array']="";              //Debug
+
+    /// Process key attributes (name, type, fields, reftable, 
+    /// reffields, comment, previous, next)
+        if (isset($xmlarr['@']['NAME'])) {
+            $this->name = trim($xmlarr['@']['NAME']);
+        } else {
+            $this->errormsg = 'Missing NAME attribute';
+            $result = false;
+        }
+
+        if (isset($xmlarr['@']['TYPE'])) {
+        /// Check for valid type
+            $type = $this->getXMLDBKeyType(trim($xmlarr['@']['TYPE']));
+            if ($type) {
+                $this->type = $type;
+            } else {
+                $this->errormsg = 'Invalid TYPE attribute';
+                $result = false;
+            }
+        } else {
+            $this->errormsg = 'Missing TYPE attribute';
+            $result = false;
+        }
+
+        if (isset($xmlarr['@']['FIELDS'])) {
+            $fields = strtolower(trim($xmlarr['@']['FIELDS']));
+            if ($fields) {
+                $fieldsarr = explode(',',$fields);
+                if ($fieldsarr) {
+                    foreach ($fieldsarr as $key => $element) {
+                        $fieldsarr [$key] = trim($element);
+                    }
+                } else {
+                    $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
+                    $result = false;
+                }
+            } else {
+                $this->errormsg = 'Empty FIELDS attribute';
+                $result = false;
+            }
+        } else {
+            $this->errormsg = 'Missing FIELDS attribute';
+            $result = false;
+        }
+    /// Finally, set the array of fields
+        $this->fields = $fieldsarr;
+
+        if (isset($xmlarr['@']['REFTABLE'])) {
+        /// Check we are in a FK
+            if ($this->type == XMLDB_KEY_FOREIGN || 
+                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) { 
+                $reftable = strtolower(trim($xmlarr['@']['REFTABLE']));
+                if (!$reftable) {
+                    $this->errormsg = 'Empty REFTABLE attribute';
+                    $result = false;
+                }
+            } else {
+                $this->errormsg = 'Wrong REFTABLE attribute (only FK can have it)';
+                $result = false;
+            }
+        } else if ($this->type == XMLDB_KEY_FOREIGN ||
+                   $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $this->errormsg = 'Missing REFTABLE attribute';
+            $result = false;
+        }
+    /// Finally, set the reftable
+        if ($this->type == XMLDB_KEY_FOREIGN ||
+            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $this->reftable = $reftable;
+        }
+
+        if (isset($xmlarr['@']['REFFIELDS'])) {
+        /// Check we are in a FK
+            if ($this->type == XMLDB_KEY_FOREIGN ||
+                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+                $reffields = strtolower(trim($xmlarr['@']['REFFIELDS']));
+                if ($reffields) {
+                    $reffieldsarr = explode(',',$reffields);
+                    if ($reffieldsarr) {
+                        foreach ($reffieldsarr as $key => $element) {
+                            $reffieldsarr [$key] = trim($element);
+                        }
+                    } else {
+                        $this->errormsg = 'Incorrect REFFIELDS attribute (comma separated of fields)';
+                        $result = false;
+                    }
+                } else {
+                    $this->errormsg = 'Empty REFFIELDS attribute';
+                    $result = false;
+                }
+            } else {
+                $this->errormsg = 'Wrong REFFIELDS attribute (only FK can have it)';
+                $result = false;
+            }
+        } else if ($this->type == XMLDB_KEY_FOREIGN ||
+                   $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $this->errormsg = 'Missing REFFIELDS attribute';
+            $result = false;
+        }
+    /// Finally, set the array of reffields
+        if ($this->type == XMLDB_KEY_FOREIGN ||
+            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $this->reffields = $reffieldsarr;
+        }
+
+        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_KEY_XXX value for the
+     * string passed as argument
+     */
+    function getXMLDBKeyType($type) {
+
+        $result = XMLDB_KEY_INCORRECT;
+        
+        switch (strtolower($type)) {
+            case 'primary':
+                $result = XMLDB_KEY_PRIMARY;
+                break;
+            case 'unique':
+                $result = XMLDB_KEY_UNIQUE;
+                break;
+            case 'foreign':
+                $result = XMLDB_KEY_FOREIGN;
+                break;
+            case 'foreign-unique':
+                $result = XMLDB_KEY_FOREIGN_UNIQUE;
+                break;
+        /// case 'check':  //Not supported
+        ///     $result = XMLDB_KEY_CHECK;
+        ///     break;
+        }
+    /// Return the normalized XMLDB_KEY
+        return $result;
+    }
+
+    /**
+     * This function returns the correct name value for the
+     * XMLDB_KEY_XXX passed as argument
+     */
+    function getXMLDBKeyName($type) {
+
+        $result = '';
+        
+        switch (strtolower($type)) {
+            case XMLDB_KEY_PRIMARY:
+                $result = 'primary';
+                break;
+            case XMLDB_KEY_UNIQUE:
+                $result = 'unique';
+                break;
+            case XMLDB_KEY_FOREIGN:
+                $result = 'foreign';
+                break;
+            case XMLDB_KEY_FOREIGN_UNIQUE:
+                $result = 'foreign-unique';
+                break;
+        /// case XMLDB_KEY_CHECK:  //Not supported
+        ///     $result = 'check';
+        ///     break;
+        }
+    /// Return the normalized name
+        return $result;
+    }
+
+    /**
+     * This function calculate and set the hash of one XMLDBKey
+     */
+     function calculateHash($recursive = false) {
+        if (!$this->loaded) {
+            $this->hash = NULL;
+        } else {
+            $key = $this->type . implode(', ', $this->fields);
+            if ($this->type == XMLDB_KEY_FOREIGN ||
+                $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+                $key .= $this->reftable . implode(', ', $this->reffields);
+            }
+                    ;
+            $this->hash = md5($key);
+        }
+    }
+
+    /** 
+     *This function will output the XML text for one key
+     */
+    function xmlOutput() {
+        $o = '';
+        $o.= '        <KEY NAME="' . $this->name . '"';
+        $o.= ' TYPE="' . $this->getXMLDBKeyName($this->type) . '"';
+        $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
+        if ($this->type == XMLDB_KEY_FOREIGN ||
+            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $o.= ' REFTABLE="' . $this->reftable . '"';
+            $o.= ' REFFIELDS="' . implode(', ', $this->reffields) . '"';
+        }
+        if ($this->comment) {
+            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+        }
+        if ($this->previous) {
+            $o.= ' PREVIOUS="' . $this->previous . '"';
+        }
+        if ($this->next) {
+            $o.= ' NEXT="' . $this->next . '"';
+        }
+        $o.= '/>' . "\n";
+
+        return $o;
+    }
+
+    /**
+     * This function will set all the attributes of the XMLDBKey object
+     * based on information passed in one ADOkey
+     */
+    function setFromADOKey($adokey) {
+
+    /// Calculate the XMLDB_KEY
+        switch (strtolower($adokey['name'])) {
+            case 'primary':
+                $this->type = XMLDB_KEY_PRIMARY;
+                break;
+            default:
+                $this->type = XMLDB_KEY_UNIQUE;
+        }
+    /// Set the fields
+        $this->fields = $adokey['columns'];
+    /// Some more fields
+        $this->loaded = true;
+        $this->changed = true;
+    }
+
+    /**     
+     * Shows info in a readable format
+     */ 
+    function readableInfo() {
+        $o = '';
+    /// type
+        $o .= $this->getXMLDBKeyName($this->type);
+    /// fields
+        $o .= ' (' . implode(', ', $this->fields) . ')';
+    /// foreign key
+        if ($this->type == XMLDB_KEY_FOREIGN ||
+            $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+            $o .= ' references ' . $this->reftable . ' (' . implode(', ', $this->reffields) . ')';
+        }
+
+        return $o;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBObject.class.php b/lib/xmldb/classes/XMLDBObject.class.php
new file mode 100644 (file)
index 0000000..efd6f1f
--- /dev/null
@@ -0,0 +1,387 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 the XMLDB base class where all the common piezes
+/// are defined
+
+class XMLDBObject {
+    var $name;
+    var $comment;
+    var $previous;
+    var $next;
+    var $hash;
+    var $loaded;
+    var $changed;
+    var $errormsg;
+
+    /**
+     * Creates one new XMLDBObject
+     */
+    function XMLDBObject($name) {
+        $this->name = $name;
+        $this->comment = NULL;
+        $this->previous = NULL;
+        $this->next = NULL;
+        $this->hash = NULL;
+        $this->loaded = false;
+        $this->changed = false;
+        $this->errormsg = NULL;
+    }
+
+    /**
+     * This function returns true/false, if the XMLDBObject has been loaded
+     */
+    function isLoaded() {
+        return $this->loaded;
+    }
+
+    /**
+     * This function returns true/false, if the XMLDBObject has changed
+     */
+    function hasChanged() {
+        return $this->changed;
+    }
+
+    /** 
+     * This function returns the comment of one XMLDBObject
+     */
+    function getComment() {
+        return $this->comment;
+    }
+
+    /** 
+     * This function returns the hash of one XMLDBObject
+     */
+    function getHash() {
+        return $this->hash;
+    }
+
+    /**
+     * This function will return the name of the previous XMLDBObject
+     */
+    function getPrevious() {
+        return $this->previous;
+    }
+
+    /**
+     * This function will return the name of the next XMLDBObject
+     */
+    function getNext() {
+        return $this->next;
+    }
+
+    /**
+     * This function will return the name of the XMLDBObject
+     */
+    function getName() {
+        return $this->name;
+    }
+
+    /**
+     * This function will return the error detected in the object
+     */
+    function getError() {
+        return $this->errormsg;
+    }
+
+    /**
+     * This function will set the comment of the XMLDB object
+     */
+    function setComment($comment) {
+        $this->comment = $comment;
+    }
+
+    /**
+     * This function will set the previous of the XMLDB object
+     */
+    function setPrevious($previous) {
+        $this->previous = $previous;
+    }
+
+    /**
+     * This function will set the next of the XMLDB object
+     */
+    function setNext($next) {
+        $this->next = $next;
+    }
+
+    /**
+     * This function will set the hash of the XMLDB object
+     */
+    function setHash($hash) {
+        $this->hash = $hash;
+    }
+
+    /**
+     * This function will set the loaded field of the XMLDB object
+     */
+    function setLoaded($loaded = true) {
+        $this->loaded = $loaded;
+    }
+
+    /**
+     * This function will set the changed field of the XMLDB object
+     */
+    function setChanged($changed = true) {
+        $this->changed = $changed;
+    }
+    /**
+     * This function will set the name field of the XMLDB object
+     */
+    function setName($name) {
+        $this->name = $name;
+    }
+
+
+    /**
+     * This function will check if one key name is ok or no (true/false)
+     * only lowercase a-z, 0-9 and _ are allowed
+     */
+    function checkName () {
+        $result = true;
+
+        if ($this->name != eregi_replace('[^a-z0-9_ -]', '', $this->name)) {
+            $result = false;
+        }
+        return $result;
+    }
+
+    /**
+     * This function will check that all the elements in one array
+     * have a correct name [a-z0-9_]
+     */
+    function checkNameValues(&$arr) {
+        $result = true;
+    /// TODO: Perhaps, add support for reserved words
+
+    /// Check the name only contains valid chars
+        if ($arr) {
+            foreach($arr as $element) {
+                if (!$element->checkName()) {
+                    $result = false;
+                }
+            }
+        }
+        return $result;
+    }
+
+    /** 
+     * This function will check that all the elements in one array
+     * have a consistent info in their previous/next fields
+     */
+    function checkPreviousNextValues(&$arr) {
+        $result = true;
+    /// Check that only one element has the previous not set
+        if ($arr) {
+            $counter = 0;
+            foreach($arr as $element) {
+                if (!$element->getPrevious()) {
+                    $counter++;
+                }
+            }
+            if ($counter != 1) {
+                $result = false;
+            }
+        }
+    /// Check that only one element has the next not set
+        if ($result && $arr) {
+            $counter = 0;
+            foreach($arr as $element) {
+                if (!$element->getNext()) {
+                    $counter++;
+                }
+            }
+            if ($counter != 1) {
+                $result = false;
+            }
+        }
+    /// Check that all the previous elements are existing elements
+        if ($result && $arr) {
+            foreach($arr as $element) {
+                if ($element->getPrevious()) {
+                    $i = $this->findObjectInArray($element->getPrevious(), $arr);
+                    if ($i === NULL) {
+                        $result = false;
+                    }
+                }
+            }
+        }
+    /// Check that all the next elements are existing elements
+        if ($result && $arr) {
+            foreach($arr as $element) {
+                if ($element->getNext()) {
+                    $i = $this->findObjectInArray($element->getNext(), $arr);
+                    if ($i === NULL) {
+                        $result = false;
+                    }
+                }
+            }
+        }
+    /// Check that there aren't duplicates in the previous values
+        if ($result && $arr) {
+            $existarr = array();
+            foreach($arr as $element) {
+                if (in_array($element->getPrevious(), $existarr)) {
+                    $result = false;
+                } else {
+                    $existarr[] = $element->getPrevious();
+                }
+            }
+        }
+    /// Check that there aren't duplicates in the next values
+        if ($result && $arr) {
+            $existarr = array();
+            foreach($arr as $element) {
+                if (in_array($element->getNext(), $existarr)) {
+                    $result = false;
+                } else {
+                    $existarr[] = $element->getNext();
+                }
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * This function will order all the elements in one array, following
+     * the previous/next rules
+     */
+    function orderElements($arr) {
+        $result = true;
+    /// Create a new array
+        $newarr = array();
+        if (!empty($arr)) {
+            $currentelement = NULL;
+        /// Get the element without previous
+            foreach($arr as $key => $element) {
+                if (!$element->getPrevious()) {
+                    $currentelement = $arr[$key];
+                    $newarr[0] = $arr[$key];
+                }
+            }
+            if (!$currentelement) {
+                $result = false;
+            }
+        /// Follow the next rules
+            $counter = 1;
+            while ($result && $currentelement->getNext()) {
+                $i = $this->findObjectInArray($currentelement->getNext(), $arr);
+                $currentelement = $arr[$i];
+                $newarr[$counter] = $arr[$i];
+                $counter++;
+            }
+        /// Compare number of elements between original and new array
+            if ($result && count($arr) != count($newarr)) {
+                $result = false;
+            }
+        /// Check that previous/next is ok (redundant but...)
+            if ($this->checkPreviousNextValues($newarr)) {
+                $result = $newarr;
+            } else {
+                $result = false;
+            }
+        } else {
+            $result = array();
+        }
+        return $result;
+    }
+
+    /**
+     * Returns the position of one object in the array.
+     */
+    function &findObjectInArray($objectname, $arr) {
+        foreach ($arr as $i => $object) {
+            if ($objectname == $object->getName()) {
+                return $i; 
+            }   
+        }   
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * This function will display a readable info about the XMLDBObject
+     * (should be implemented inside each XMLDBxxx object)
+     */
+    function readableInfo() {
+        return get_class($this);
+    }
+
+    /**
+     * Returns one array of elements from one comma separated string, 
+     * supporting quoted strings containing commas and concat function calls
+     */
+    function comma2array($string) {
+
+        $arr = array();
+
+        $foundquotes  = array();
+        $foundconcats = array();
+
+    /// Extract all the concat elements from the string
+        preg_match_all("/(CONCAT\(.*?\))/is", $string, $matches);
+        foreach (array_unique($matches[0]) as $key=>$value) {
+            $foundconcats['<#'.$key.'#>'] = $value;
+        }
+        if (!empty($foundconcats)) {
+            $string = str_replace($foundconcats,array_keys($foundconcats),$string);
+        }
+
+    /// Extract all the quoted elements from the string
+        preg_match_all("/('.*?')/is", $string, $matches);
+        foreach (array_unique($matches[0]) as $key=>$value) {
+            $foundquotes['<%'.$key.'%>'] = $value;
+        }
+        if (!empty($foundquotes)) {
+            $string = str_replace($foundquotes,array_keys($foundquotes),$string);
+        }
+
+    /// Explode safely the string
+        $arr = explode (',', $string);
+
+    /// Put the concat and quoted elements back again, triming every element
+        if ($arr) {
+            foreach ($arr as $key => $element) {
+            /// Clear some spaces
+                $element = trim($element);
+            /// Replace the quoted elements if exists
+                if (!empty($foundquotes)) {
+                    $element = str_replace(array_keys($foundquotes), $foundquotes, $element);
+                }
+            /// Replace the concat elements if exists
+                if (!empty($foundconcats)) {
+                    $element = str_replace(array_keys($foundconcats), $foundconcats, $element);
+                }
+                $arr[$key] = $element;
+            }
+        }
+
+        return $arr;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBStatement.class.php b/lib/xmldb/classes/XMLDBStatement.class.php
new file mode 100644 (file)
index 0000000..7592803
--- /dev/null
@@ -0,0 +1,365 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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';
+            $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';
+                $result = false;
+            }
+        } else {
+            $this->errormsg = 'Missing TYPE attribute';
+            $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';
+                        $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';
+                        $result = false;
+                    }
+                    if ($result && count($fields) == 0) {
+                        $this->errormsg = 'Missing fields in sentence "' . $sentence . '"';
+                        $result = false;
+                    }
+                    if ($result && count($values) == 0) {
+                        $this->errormsg = 'Missing values in sentence "' . $sentence . '"';
+                        $result = false;
+                    }
+                    if ($result && count($fields) != count($values)) {
+                        $this->errormsg = 'Incorrect number of fields (' .implode(', ', $fields) . ') or values (' . implode(', ', $values) . ')';
+                        $result = false;
+                    }
+                }
+            }
+        } else {
+        /// Sentences different from INSERT are not valid for now
+            $this->errormsg = 'Only INSERT statements are supported';
+            $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;
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBStructure.class.php b/lib/xmldb/classes/XMLDBStructure.class.php
new file mode 100644 (file)
index 0000000..73eea8c
--- /dev/null
@@ -0,0 +1,697 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 structure
+
+class XMLDBStructure extends XMLDBObject {
+    var $path;
+    var $version;
+    var $tables;
+    var $statements;
+
+    /**
+     * Creates one new XMLDBStructure
+     */
+    function XMLDBStructure($name) {
+        parent::XMLDBObject($name);
+        $this->path = NULL;
+        $this->version = NULL;
+        $this->tables = array();
+        $this->statements = array();
+    }
+
+    /**
+     * Returns the path of the structure
+     */
+    function getPath() {
+        return $this->path;
+    }
+
+    /**
+     * Returns the version of the structure
+     */
+    function getVersion() {
+        return $this->version;
+    }
+
+    /**
+     * Returns one XMLDBTable
+     */
+    function &getTable($tablename) {
+        $i = $this->findTableInArray($tablename);
+        if ($i !== NULL) { 
+            return $this->tables[$i];
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the position of one table in the array.
+     */
+    function &findTableInArray($tablename) {
+        foreach ($this->tables as $i => $table) {
+            if ($tablename == $table->getName()) {
+                return $i;
+            }
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the position of one statement in the array.
+     */
+    function &findStatementInArray($statementname) {
+        foreach ($this->statements as $i => $statement) {
+            if ($statementname == $statement->getName()) {
+                return $i;
+            }
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * This function will reorder the array of tables
+     */
+    function orderTables() {
+        $result =& $this->orderElements($this->tables);
+        if ($result) {
+            $this->setTables($result);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This function will reorder the array of statements
+     */
+    function orderStatements() {
+        $result =& $this->orderElements($this->statements);
+        if ($result) {
+            $this->setStatements($result);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the tables of the structure
+     */
+    function &getTables() {
+        return $this->tables;
+    }
+
+    /**
+     * Returns one XMLDBStatement
+     */
+    function &getStatement($statementname) {
+        $i = $this->findStatementInArray($statementname);
+        if ($i !== NULL) { 
+            return $this->statements[$i];
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the statements of the structure
+     */
+    function &getStatements() {
+        return $this->statements;
+    }
+
+    /** 
+     * Set the structure version
+     */
+    function setVersion($version) {
+        $this->version = $version;
+    }
+
+    /**
+     * Add one table to the structure, allowing to specify the desired order
+     * If it's not specified, then the table is added at the end.
+     */
+    function addTable(&$table, $after=NULL) {
+
+    /// Calculate the previous and next tables
+        $prevtable = NULL;
+        $nexttable = NULL;
+
+        if (!$after) {
+            $alltables =& $this->getTables();
+            if ($alltables) {
+                end($alltables);
+                $prevtable =& $alltables[key($alltables)];
+            }
+        } else {
+            $prevtable =& $this->getTable($after);
+        }
+        if ($prevtable && $prevtable->getNext()) {
+            $nexttable =& $this->getTable($prevtable->getNext());
+        }
+
+    /// Set current table previous and next attributes
+        if ($prevtable) {
+            $table->setPrevious($prevtable->getName());
+            $prevtable->setNext($table->getName());
+        }
+        if ($nexttable) {
+            $table->setNext($nexttable->getName());
+            $nexttable->setPrevious($table->getName());
+        }
+    /// Some more attributes
+        $table->setLoaded(true);
+        $table->setChanged(true);
+    /// Add the new table
+        $this->tables[] =& $table;
+    /// Reorder the whole structure
+        $this->orderTables($this->tables);
+    /// Recalculate the hash
+        $this->calculateHash(true);
+    /// We have one new table, so the structure has changed
+        $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+        $this->setChanged(true);
+    }
+
+    /**
+     * Add one statement to the structure, allowing to specify the desired order
+     * If it's not specified, then the statement is added at the end.
+     */
+    function addStatement(&$statement, $after=NULL) {
+
+    /// Calculate the previous and next tables
+        $prevstatement = NULL;
+        $nextstatement = NULL;
+
+        if (!$after) {
+            $allstatements =& $this->getStatements();
+            if ($allstatements) {
+                end($allstatements);
+                $prevstatement =& $allstatements[key($allstatements)];
+            }
+        } else {
+            $prevstatement =& $this->getStatement($after);
+        }
+        if ($prevstatement && $prevstatement->getNext()) {
+            $nextstatement =& $this->getStatement($prevstatement->getNext());
+        }
+
+    /// Set current statement previous and next attributes
+        if ($prevstatement) {
+            $statement->setPrevious($prevstatement->getName());
+            $prevstatement->setNext($statement->getName());
+        }
+        if ($nextstatement) {
+            $statement->setNext($nextstatement->getName());
+            $nextstatement->setPrevious($statement->getName());
+        }
+    /// Some more attributes
+        $statement->setLoaded(true);
+        $statement->setChanged(true);
+    /// Add the new statement
+        $this->statements[] =& $statement;
+    /// Reorder the whole structure
+        $this->orderStatements($this->statements);
+    /// Recalculate the hash
+        $this->calculateHash(true);
+    /// We have one new statement, so the structure has changed
+        $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+        $this->setChanged(true);
+    }
+
+    /**
+     * Delete one table from the Structure
+     */
+    function deleteTable($tablename) {
+
+        $table =& $this->getTable($tablename);
+        if ($table) {
+            $i = $this->findTableInArray($tablename);
+            $prevtable = NULL;
+            $nexttable = NULL;
+        /// Look for prev and next table
+            $prevtable =& $this->getTable($table->getPrevious());
+            $nexttable =& $this->getTable($table->getNext());
+        /// Change their previous and next attributes
+            if ($prevtable) {
+                $prevtable->setNext($table->getNext());
+            }
+            if ($nexttable) {
+                $nexttable->setPrevious($table->getPrevious());
+            }
+        /// Delete the table
+            unset($this->tables[$i]);
+        /// Reorder the tables
+            $this->orderTables($this->tables);
+        /// Recalculate the hash
+            $this->calculateHash(true);
+        /// We have one deleted table, so the structure has changed
+            $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+            $this->setChanged(true);
+        }
+    }
+
+    /**
+     * Delete one statement from the Structure
+     */
+    function deleteStatement($statementname) {
+
+        $statement =& $this->getStatement($statementname);
+        if ($statement) {
+            $i = $this->findStatementInArray($statementname);
+            $prevstatement = NULL;
+            $nextstatement = NULL;
+        /// Look for prev and next statement
+            $prevstatement =& $this->getStatement($statement->getPrevious());
+            $nextstatement =& $this->getStatement($statement->getNext());
+        /// Change their previous and next attributes
+            if ($prevstatement) {
+                $prevstatement->setNext($statement->getNext());
+            }
+            if ($nextstatement) {
+                $nextstatement->setPrevious($statement->getPrevious());
+            }
+        /// Delete the statement
+            unset($this->statements[$i]);
+        /// Reorder the statements
+            $this->orderStatements($this->statements);
+        /// Recalculate the hash
+            $this->calculateHash(true);
+        /// We have one deleted statement, so the structure has changed
+            $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+            $this->setChanged(true);
+        }
+    }
+
+    /** 
+     * Set the tables
+     */
+    function setTables(&$tables) {
+        $this->tables = $tables;
+    }
+
+    /** 
+     * Set the statements
+     */
+    function setStatements(&$statements) {
+        $this->statements = $statements;
+    }
+
+    /**
+     * Load data from XML to the structure
+     */
+    function arr2XMLDBStructure($xmlarr) {
+
+        $result = true;
+
+    /// Debug the structure
+    /// traverse_xmlize($xmlarr);                   //Debug
+    /// print_object ($GLOBALS['traverse_array']);  //Debug
+    /// $GLOBALS['traverse_array']="";              //Debug
+
+    /// Process structure attributes (path, comment and version)
+        if (isset($xmlarr['XMLDB']['@']['PATH'])) {
+            $this->path = trim($xmlarr['XMLDB']['@']['PATH']);
+        } else {
+            $this->errormsg = 'Missing PATH attribute';
+            $result = false;
+        }
+        if (isset($xmlarr['XMLDB']['@']['VERSION'])) {
+            $this->version = trim($xmlarr['XMLDB']['@']['VERSION']);
+        } else {
+            $this->errormsg = 'Missing VERSION attribute';
+            $result = false;
+        }
+        if (isset($xmlarr['XMLDB']['@']['COMMENT'])) {
+            $this->comment = trim($xmlarr['XMLDB']['@']['COMMENT']);
+        } else {
+            $this->errormsg = 'Missing COMMENT attribute';
+            $result = false;
+        }
+
+    /// Iterate over tables
+        if (isset($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'])) {
+            foreach ($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'] as $xmltable) {
+                if (!$result) { //Skip on error
+                    continue;
+                }
+                $name = trim($xmltable['@']['NAME']);
+                $table = new XMLDBTable($name);
+                $table->arr2XMLDBTable($xmltable);
+                $this->tables[] = $table;
+                if (!$table->isLoaded()) {
+                    $this->errormsg = 'Problem loading table ' . $name;
+                    $result = false;
+                }
+            }
+        } else {
+            $this->errormsg = 'Missing TABLES section';
+            $result = false;
+        }
+
+    /// Perform some general checks over tables
+        if ($result && $this->tables) {
+        /// Check tables names are ok (lowercase, a-z _-)
+            if (!$this->checkNameValues($this->tables)) {
+                $this->errormsg = 'Some TABLES name values are incorrect';
+                $result = false;
+            }
+        /// Check previous & next are ok (duplicates and existing tables)
+            if ($result && !$this->checkPreviousNextValues($this->tables)) {
+                $this->errormsg = 'Some TABLES previous/next values are incorrect';
+                $result = false;
+            }
+        /// Order tables
+            if ($result && !$this->orderTables($this->tables)) {
+                $this->errormsg = 'Error ordering the tables';
+                $result = false;
+            }
+        }
+
+    /// Iterate over statements
+        if (isset($xmlarr['XMLDB']['#']['STATEMENTS']['0']['#']['STATEMENT'])) {
+            foreach ($xmlarr['XMLDB']['#']['STATEMENTS']['0']['#']['STATEMENT'] as $xmlstatement) {
+                if (!$result) { //Skip on error
+                    continue;
+                }
+                $name = trim($xmlstatement['@']['NAME']);
+                $statement = new XMLDBStatement($name);
+                $statement->arr2XMLDBStatement($xmlstatement);
+                $this->statements[] = $statement;
+                if (!$statement->isLoaded()) {
+                    $this->errormsg = 'Problem loading statement ' . $name;
+                    $result = false;
+                }
+            }
+        }
+    
+    /// Perform some general checks over statements
+        if ($result && $this->statements) {
+        /// Check statements names are ok (lowercase, a-z _-)
+            if (!$this->checkNameValues($this->statements)) {
+                $this->errormsg = 'Some STATEMENTS name values are incorrect';
+                $result = false;
+            }
+        /// Check previous & next are ok (duplicates and existing statements)
+            if ($result && !$this->checkPreviousNextValues($this->statements)) {
+                $this->errormsg = 'Some STATEMENTS previous/next values are incorrect';
+                $result = false;
+            }
+        /// Order statements
+            if ($result && !$this->orderStatements($this->statements)) {
+                $this->errormsg = 'Error ordering the statements';
+                $result = false;
+            }
+        }
+
+    /// Set some attributes
+        if ($result) {
+            $this->loaded = true;
+        }
+        $this->calculateHash();
+        return $result;
+    }
+
+    /**
+     * This function calculate and set the hash of one XMLDBStructure
+     */
+     function calculateHash($recursive = false) {
+        if (!$this->loaded) {
+            $this->hash = NULL;
+        } else {
+            $key = $this->name . $this->path . $this->comment;
+            if ($this->tables) {
+                foreach ($this->tables as $tbl) {
+                    $table =& $this->getTable($tbl->getName());
+                    if ($recursive) {
+                        $table->calculateHash($recursive);
+                    }
+                    $key .= $table->getHash();
+                }
+            }
+            if ($this->statements) {
+                foreach ($this->statements as $sta) {
+                    $statement =& $this->getStatement($sta->getName());
+                    if ($recursive) {
+                        $statement->calculateHash($recursive);
+                    }
+                    $key .= $statement->getHash();
+                }
+            }
+            $this->hash = md5($key);
+        }
+    }
+
+    /** 
+     * This function will output the XML text for one structure
+     */
+    function xmlOutput() {
+        $o = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n";
+        $o.= '<XMLDB PATH="' . $this->path . '"';
+        $o.= ' VERSION="' . $this->version . '"';
+        if ($this->comment) {
+            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+        }
+        $o.= '>' . "\n";
+    /// Now the tables
+        if ($this->tables) {
+            $o.= '  <TABLES>' . "\n";
+            foreach ($this->tables as $table) {
+                $o.= $table->xmlOutput();
+            }
+            $o.= '  </TABLES>' . "\n";
+        }
+    /// Now the statements
+        if ($this->statements) {
+            $o.= '  <STATEMENTS>' . "\n";
+            foreach ($this->statements as $statement) {
+                $o.= $statement->xmlOutput();
+            }
+            $o.= '  </STATEMENTS>' . "\n";
+        }
+        $o.= '</XMLDB>';
+
+        return $o;
+    }
+
+    /**
+     * This function returns the number of uses of one table inside
+     * a whole XMLDStructure. Useful to detect if the table must be 
+     * locked. Return false if no uses are found.
+     */
+    function getTableUses($tablename) {
+
+        $uses = array();
+
+    /// Check if some foreign key in the whole structure is using it
+    /// (by comparing the reftable with the tablename)
+        $alltables = $this->getTables();
+        if ($alltables) {
+            foreach ($alltables as $table) {
+                $keys = $table->getKeys();
+                if ($keys) {
+                    foreach ($keys as $key) {
+                        if ($key->getType() == XMLDB_KEY_FOREIGN) {
+                            if ($tablename == $key->getRefTable()) {
+                                $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    /// Return result
+        if (!empty($uses)) {
+            return $uses;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This function returns the number of uses of one field inside
+     * a whole XMLDBStructure. Useful to detect if the field must be 
+     * locked. Return false if no uses are found.
+     */
+    function getFieldUses($tablename, $fieldname) {
+
+        $uses = array();
+
+    /// Check if any key in the table is using it
+        $table = $this->getTable($tablename);
+        if ($keys = $table->getKeys()) {
+            foreach ($keys as $key) {
+                if (in_array($fieldname, $key->getFields()) ||
+                    in_array($fieldname, $key->getRefFields())) {
+                        $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+                }
+            }          
+        }
+    /// Check if any index in the table is using it
+        $table = $this->getTable($tablename);
+        if ($indexes = $table->getIndexes()) {
+            foreach ($indexes as $index) {
+                if (in_array($fieldname, $index->getFields())) {
+                    $uses[] = 'table ' . $table->getName() . ' index ' . $index->getName();
+                }
+            }          
+        }
+    /// Check if some foreign key in the whole structure is using it
+    /// By comparing the reftable and refields with the field)
+        $alltables = $this->getTables();
+        if ($alltables) {
+            foreach ($alltables as $table) {
+                $keys = $table->getKeys();
+                if ($keys) {
+                    foreach ($keys as $key) {
+                        if ($key->getType() == XMLDB_KEY_FOREIGN) {
+                            if ($tablename == $key->getRefTable()) {
+                                $reffieds = $key->getRefFields();
+                                if (in_array($fieldname, $key->getRefFields())) {
+                                    $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    /// Return result
+        if (!empty($uses)) {
+            return $uses;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This function returns the number of uses of one key inside
+     * a whole XMLDBStructure. Useful to detect if the key must be 
+     * locked. Return false if no uses are found.
+     */
+    function getKeyUses($tablename, $keyname) {
+
+        $uses = array();
+
+    /// Check if some foreign key in the whole structure is using it
+    /// (by comparing the reftable and reffields with the fields in the key)
+        $mytable = $this->getTable($tablename);
+        $mykey = $mytable->getKey($keyname);
+        $alltables = $this->getTables();
+        if ($alltables && $mykey) {
+            foreach ($alltables as $table) {
+                $allkeys = $table->getKeys();
+                if ($allkeys) {
+                    foreach ($allkeys as $key) {
+                        if ($key->getType() != XMLDB_KEY_FOREIGN) {
+                            continue;
+                        }
+                        if ($key->getRefTable() == $tablename &&
+                            implode(',', $key->getRefFields()) == implode(',', $mykey->getFields())) {
+                                $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+                        }
+                    }
+                }
+            }
+        }
+
+    /// Return result
+        if (!empty($uses)) {
+            return $uses;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This function returns the number of uses of one index inside
+     * a whole XMLDBStructure. Useful to detect if the index must be 
+     * locked. Return false if no uses are found.
+     */
+    function getIndexUses($tablename, $indexname) {
+
+        $uses = array();
+
+    /// Nothing to check, beause indexes haven't uses! Leave it here
+    /// for future checks...
+
+    /// Return result
+        if (!empty($uses)) {
+            return $uses;
+        } else {
+            return false;
+        }
+    }
+
+    /** 
+     * This function will return all the errors found in one structure
+     * looking recursively inside each table/statement. Returns
+     * an array of errors or false
+     */
+    function getAllErrors() {
+
+        $errors = array();
+    /// First the structure itself
+        if ($this->getError()) {
+            $errors[] = $this->getError();
+        }
+    /// Delegate to tables
+        if ($tables = $this->getTables()) {
+            foreach ($tables as $table) {
+                if ($tableerrors = $table->getAllErrors()) {
+    
+                }
+            }
+        /// Add them to the errors array
+            if ($tableerrors) {
+                $errors = array_merge($errors, $tableerrors);
+            }
+        }
+    /// Delegate to statements
+        if ($statements = $this->getStatements()) {
+            foreach ($statements as $statement) {
+                if ($statement->getError()) {
+                    $errors[] = $statement->getError();
+                }
+            }
+        }
+    /// Return decision
+        if (count($errors)) {
+            return $errors;
+        } else {
+            return false;
+        }
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/XMLDBTable.class.php b/lib/xmldb/classes/XMLDBTable.class.php
new file mode 100644 (file)
index 0000000..fd917ba
--- /dev/null
@@ -0,0 +1,729 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 table
+
+class XMLDBTable extends XMLDBObject {
+    var $fields;
+    var $keys;
+    var $indexes;
+
+    /**
+     * Creates one new XMLDBTable
+     */
+    function XMLDBTable($name) {
+        parent::XMLDBObject($name);
+        $this->fields = array();
+        $this->keys = array();
+        $this->indexes = array();
+    }
+
+    /**
+     * Add one field to the table, allowing to specify the desired  order
+     * If it's not specified, then the field is added at the end
+     */
+    function addField(&$field, $after=NULL) {
+
+    /// Calculate the previous and next fields
+        $prevfield = NULL;
+        $nextfield = NULL;
+
+        if (!$after) {
+            $allfields =& $this->getFields();
+            if (!empty($allfields)) {
+                end($allfields);
+                $prevfield =& $allfields[key($allfields)];
+            }
+        } else {
+            $prevfield =& $this->getField($after);
+        }
+        if ($prevfield && $prevfield->getNext()) {
+            $nextfield =& $this->getField($prevfield->getNext());
+        }
+
+    /// Set current field previous and next attributes
+        if ($prevfield) {
+            $field->setPrevious($prevfield->getName());
+            $prevfield->setNext($field->getName());
+        }
+        if ($nextfield) {
+            $field->setNext($nextfield->getName());
+            $nextfield->setPrevious($field->getName());
+        }
+    /// Some more attributes
+        $field->setLoaded(true);
+        $field->setChanged(true);
+    /// Add the new field
+        $this->fields[] = $field;
+    /// Reorder the field
+        $this->orderFields($this->fields);
+    /// Recalculate the hash
+        $this->calculateHash(true);
+    /// We have one new field, so the table has changed
+        $this->setChanged(true);
+    }
+
+    /**
+     * Add one key to the table, allowing to specify the desired  order
+     * If it's not specified, then the key is added at the end
+     */
+    function addKey(&$key, $after=NULL) {
+
+    /// Calculate the previous and next keys
+        $prevkey = NULL;
+        $nextkey = NULL;
+
+        if (!$after) {
+            $allkeys =& $this->getKeys();
+            if (!empty($allkeys)) {
+                end($allkeys);
+                $prevkey =& $allkeys[key($allkeys)];
+            }
+        } else {
+            $prevkey =& $this->getKey($after);
+        }
+        if ($prevkey && $prevkey->getNext()) {
+            $nextkey =& $this->getKey($prevkey->getNext());
+        }
+
+    /// Set current key previous and next attributes
+        if ($prevkey) {
+            $key->setPrevious($prevkey->getName());
+            $prevkey->setNext($key->getName());
+        }
+        if ($nextkey) {
+            $key->setNext($nextkey->getName());
+            $nextkey->setPrevious($key->getName());
+        }
+    /// Some more attributes
+        $key->setLoaded(true);
+        $key->setChanged(true);
+    /// Add the new key
+        $this->keys[] = $key;
+    /// Reorder the keys
+        $this->orderKeys($this->keys);
+    /// Recalculate the hash
+        $this->calculateHash(true);
+    /// We have one new field, so the table has changed
+        $this->setChanged(true);
+    }
+
+    /**
+     * Add one index to the table, allowing to specify the desired  order
+     * If it's not specified, then the index is added at the end
+     */
+    function addIndex(&$index, $after=NULL) {
+
+    /// Calculate the previous and next indexes
+        $previndex = NULL;
+        $nextindex = NULL;
+
+        if (!$after) {
+            $allindexes =& $this->getIndexes();
+            if (!empty($allindexes)) {
+                end($allindexes);
+                $previndex =& $allindexes[key($allindexes)];
+            }
+        } else {
+            $previndex =& $this->getIndex($after);
+        }
+        if ($previndex && $previndex->getNext()) {
+            $nextindex =& $this->getIndex($previndex->getNext());
+        }
+
+    /// Set current index previous and next attributes
+        if ($previndex) {
+            $index->setPrevious($previndex->getName());
+            $previndex->setNext($index->getName());
+        }
+        if ($nextindex) {
+            $index->setNext($nextindex->getName());
+            $nextindex->setPrevious($index->getName());
+        }
+
+    /// Some more attributes
+        $index->setLoaded(true);
+        $index->setChanged(true);
+    /// Add the new index
+        $this->indexes[] = $index;
+    /// Reorder the indexes
+        $this->orderIndexes($this->indexes);
+    /// Recalculate the hash
+        $this->calculateHash(true);
+    /// We have one new index, so the table has changed
+        $this->setChanged(true);
+    }
+
+    /**
+     * This function will return the array of fields in the table
+     */
+    function &getFields() {
+        return $this->fields;
+    }
+
+    /**
+     * This function will return the array of keys in the table
+     */
+    function &getKeys() {
+        return $this->keys;
+    }
+
+    /**
+     * This function will return the array of indexes in the table
+     */
+    function &getIndexes() {
+        return $this->indexes;
+    }
+
+    /**
+     * Returns one XMLDBField
+     */
+    function &getField($fieldname) {
+        $i = $this->findFieldInArray($fieldname);
+        if ($i !== NULL) {
+            return $this->fields[$i];
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the position of one field in the array.
+     */
+    function &findFieldInArray($fieldname) {
+        foreach ($this->fields as $i => $field) {
+            if ($fieldname == $field->getName()) {
+                return $i;
+            }
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * This function will reorder the array of fields
+     */
+    function orderFields() {
+        $result =& $this->orderElements($this->fields);
+        if ($result) {
+            $this->setFields($result);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns one XMLDBKey
+     */
+    function &getKey($keyname) {
+        $i = $this->findKeyInArray($keyname);
+        if ($i !== NULL) {
+            return $this->keys[$i];
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the position of one key in the array.
+     */
+    function &findKeyInArray($keyname) {
+        foreach ($this->keys as $i => $key) {
+            if ($keyname == $key->getName()) {
+                return $i;
+            }
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * This function will reorder the array of keys
+     */
+    function orderKeys() {
+        $result =& $this->orderElements($this->keys);
+        if ($result) {
+            $this->setKeys($result);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns one XMLDBIndex
+     */
+    function &getIndex($indexname) {
+        $i = $this->findIndexInArray($indexname);
+        if ($i !== NULL) {
+            return $this->indexes[$i];
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * Returns the position of one index in the array.
+     */
+    function &findIndexInArray($indexname) {
+        foreach ($this->indexes as $i => $index) {
+            if ($indexname == $index->getName()) {
+                return $i;
+            }
+        }
+        $null = NULL;
+        return $null;
+    }
+
+    /**
+     * This function will reorder the array of indexes
+     */
+    function orderIndexes() {
+        $result =& $this->orderElements($this->indexes);
+        if ($result) {
+            $this->setIndexes($result);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This function will set the array of fields in the table
+     */
+    function setFields($fields) {
+        $this->fields = $fields;
+    }
+
+    /**
+     * This function will set the array of keys in the table
+     */
+    function setKeys($keys) {
+        $this->keys = $keys;
+    }
+
+    /**
+     * This function will set the array of indexes in the table
+     */
+    function setIndexes($indexes) {
+        $this->indexes = $indexes;
+    }
+
+    /**
+     * Delete one field from the table
+     */
+    function deleteField($fieldname) {
+
+        $field =& $this->getField($fieldname);
+        if ($field) {
+            $i = $this->findFieldInArray($fieldname);
+            $prevfield = NULL;
+            $nextfield = NULL;
+        /// Look for prev and next field
+            $prevfield =& $this->getField($field->getPrevious());
+            $nextfield =& $this->getField($field->getNext());
+        /// Change their previous and next attributes
+            if ($prevfield) {
+                $prevfield->setNext($field->getNext());
+            }
+            if ($nextfield) {
+                $nextfield->setPrevious($field->getPrevious());
+            }
+        /// Delete the field
+            unset($this->fields[$i]);
+        /// Reorder the whole structure
+            $this->orderFields($this->fields);
+        /// Recalculate the hash
+            $this->calculateHash(true);
+        /// We have one deleted field, so the table has changed
+            $this->setChanged(true);
+        }
+    }
+
+    /**
+     * Delete one key from the table
+     */
+    function deleteKey($keyname) {
+
+        $key =& $this->getKey($keyname);
+        if ($key) {
+            $i = $this->findKeyInArray($keyname);
+            $prevkey = NULL;
+            $nextkey = NULL;
+        /// Look for prev and next key
+            $prevkey =& $this->getKey($key->getPrevious());
+            $nextkey =& $this->getKey($key->getNext());
+        /// Change their previous and next attributes
+            if ($prevkey) {
+                $prevkey->setNext($key->getNext());
+            }
+            if ($nextkey) {
+                $nextkey->setPrevious($key->getPrevious());
+            }
+        /// Delete the key
+            unset($this->keys[$i]);
+        /// Reorder the Keys
+            $this->orderKeys($this->keys);
+        /// Recalculate the hash
+            $this->calculateHash(true);
+        /// We have one deleted key, so the table has changed
+            $this->setChanged(true);
+        }
+    }
+
+    /**
+     * Delete one index from the table
+     */
+    function deleteIndex($indexname) {
+
+        $index =& $this->getIndex($indexname);
+        if ($index) {
+            $i = $this->findIndexInArray($indexname);
+            $previndex = NULL;
+            $nextindex = NULL;
+        /// Look for prev and next index
+            $previndex =& $this->getIndex($index->getPrevious());
+            $nextindex =& $this->getIndex($index->getNext());
+        /// Change their previous and next attributes
+            if ($previndex) {
+                $previndex->setNext($index->getNext());
+            }
+            if ($nextindex) {
+                $nextindex->setPrevious($index->getPrevious());
+            }
+        /// Delete the index
+            unset($this->indexes[$i]);
+        /// Reorder the indexes
+            $this->orderIndexes($this->indexes);
+        /// Recalculate the hash
+            $this->calculateHash(true);
+        /// We have one deleted index, so the table has changed
+            $this->setChanged(true);
+        }
+    }
+
+    /**
+     * Load data from XML to the table
+     */
+    function arr2XMLDBTable($xmlarr) {
+
+        $result = true;
+
+    /// Debug the table
+    /// traverse_xmlize($xmlarr);                   //Debug
+    /// print_object ($GLOBALS['traverse_array']);  //Debug
+    /// $GLOBALS['traverse_array']="";              //Debug
+
+    /// Process table attributes (name, comment, previoustable and nexttable)
+        if (isset($xmlarr['@']['NAME'])) {
+            $this->name = trim($xmlarr['@']['NAME']);
+        } else {
+            $this->errormsg = 'Missing NAME attribute';
+            $result = false;
+        }
+        if (isset($xmlarr['@']['COMMENT'])) {
+            $this->comment = trim($xmlarr['@']['COMMENT']);
+        } else {
+            $this->errormsg = 'Missing COMMENT attribute';
+            $result = false;
+        }
+        if (isset($xmlarr['@']['PREVIOUS'])) {
+            $this->previous = trim($xmlarr['@']['PREVIOUS']);
+        }
+        if (isset($xmlarr['@']['NEXT'])) {
+            $this->next = trim($xmlarr['@']['NEXT']);
+        }
+
+    /// Iterate over fields
+        if (isset($xmlarr['#']['FIELDS']['0']['#']['FIELD'])) {
+            foreach ($xmlarr['#']['FIELDS']['0']['#']['FIELD'] as $xmlfield) {
+                if (!$result) { //Skip on error
+                    continue;
+                }
+                $name = trim($xmlfield['@']['NAME']);
+                $field = new XMLDBField($name);
+                $field->arr2XMLDBField($xmlfield);
+                $this->fields[] = $field;
+                if (!$field->isLoaded()) {
+                    $this->errormsg = 'Problem loading field ' . $name;
+                    $result = false;
+                }
+            }
+        } else {
+            $this->errormsg = 'Missing FIELDS section';
+            $result = false;
+        }
+
+    /// Perform some general checks over fields
+        if ($result && $this->fields) {
+        /// Check field names are ok (lowercase, a-z _-)
+            if (!$this->checkNameValues($this->fields)) {
+                $this->errormsg = 'Some FIELDS name values are incorrect';
+                $result = false;
+            }
+        /// Check previous & next are ok (duplicates and existing fields)
+            if ($result && !$this->checkPreviousNextValues($this->fields)) {
+                $this->errormsg = 'Some FIELDS previous/next values are incorrect';
+                $result = false;
+            }
+        /// Order fields
+            if ($result && !$this->orderFields($this->fields)) {
+                $this->errormsg = 'Error ordering the fields';
+                $result = false;
+            }
+        }
+
+    /// Iterate over keys
+        if (isset($xmlarr['#']['KEYS']['0']['#']['KEY'])) {
+            foreach ($xmlarr['#']['KEYS']['0']['#']['KEY'] as $xmlkey) {
+                if (!$result) { //Skip on error
+                    continue;
+                }
+                $name = trim($xmlkey['@']['NAME']);
+                $key = new XMLDBKey($name);
+                $key->arr2XMLDBKey($xmlkey);
+                $this->keys[] = $key;
+                if (!$key->isLoaded()) {
+                    $this->errormsg = 'Problem loading key ' . $name;
+                    $result = false;
+                }
+            }
+        } else {
+            $this->errormsg = 'Missing KEYS section (at least one PK must exist)';
+            $result = false;
+        }
+
+    /// Perform some general checks over keys
+        if ($result && $this->keys) {
+        /// Check keys names are ok (lowercase, a-z _-)
+            if (!$this->checkNameValues($this->keys)) {
+                $this->errormsg = 'Some KEYS name values are incorrect';
+                $result = false;
+            }
+        /// Check previous & next are ok (duplicates and existing keys)
+            if ($result && !$this->checkPreviousNextValues($this->keys)) {
+                $this->errormsg = 'Some KEYS previous/next values are incorrect';
+                $result = false;
+            }
+        /// Order keys
+            if ($result && !$this->orderKeys($this->keys)) {
+                $this->errormsg = 'Error ordering the keys';
+                $result = false;
+            }
+        /// TODO: Only one PK
+        /// TODO: Not keys with repeated fields
+        /// TODO: Check fields and reffieds exist in table
+        }
+
+    /// Iterate over indexes
+        if (isset($xmlarr['#']['INDEXES']['0']['#']['INDEX'])) {
+            foreach ($xmlarr['#']['INDEXES']['0']['#']['INDEX'] as $xmlindex) {
+                if (!$result) { //Skip on error
+                    continue;
+                }
+                $name = trim($xmlindex['@']['NAME']);
+                $index = new XMLDBIndex($name);
+                $index->arr2XMLDBIndex($xmlindex);
+                $this->indexes[] = $index;
+                if (!$index->isLoaded()) {
+                    $this->errormsg = 'Problem loading index ' . $name;
+                    $result = false;
+                }
+            }
+        }
+
+    /// Perform some general checks over indexes
+        if ($result && $this->indexes) {
+        /// Check field names are ok (lowercase, a-z _-)
+            if (!$this->checkNameValues($this->indexes)) {
+                $this->errormsg = 'Some INDEXES name values are incorrect';
+                $result = false;
+            }
+        /// Check previous & next are ok (duplicates and existing INDEXES)
+            if ($result && !$this->checkPreviousNextValues($this->indexes)) {
+                $this->errormsg = 'Some INDEXES previous/next values are incorrect';
+                $result = false;
+            }
+        /// Order indexes
+            if ($result && !$this->orderIndexes($this->indexes)) {
+                $this->errormsg = 'Error ordering the indexes';
+                $result = false;
+            }
+        /// TODO: Not indexes with repeated fields
+        /// TODO: Check fields exist in table
+        }
+
+    /// Set some attributes
+        if ($result) {
+            $this->loaded = true;
+        }
+        $this->calculateHash();
+        return $result;
+    }
+
+    /**
+     * This function calculate and set the hash of one XMLDBTable
+     */
+     function calculateHash($recursive = false) {
+        if (!$this->loaded) {
+            $this->hash = NULL;
+        } else {
+            $key = $this->name . $this->comment;
+            if ($this->fields) {
+                foreach ($this->fields as $fie) {
+                    $field =& $this->getField($fie->getName());
+                    if ($recursive) {
+                        $field->calculateHash($recursive);
+                    }
+                    $key .= $field->getHash();
+                }
+            }
+            if ($this->keys) {
+                foreach ($this->keys as $ke) {
+                    $k =& $this->getKey($ke->getName());
+                    if ($recursive) {
+                        $k->calculateHash($recursive);
+                    }
+                    $key .= $k->getHash();
+                }
+            }
+            if ($this->indexes) {
+                foreach ($this->indexes as $in) {
+                    $index =& $this->getIndex($in->getName());
+                    if ($recursive) {
+                        $index->calculateHash($recursive);
+                    }
+                    $key .= $index->getHash();
+                }
+            }
+            $this->hash = md5($key);
+        }
+    }
+
+    /** 
+     * This function will output the XML text for one table
+     */
+    function xmlOutput() {
+        $o = '';
+        $o.= '    <TABLE NAME="' . $this->name . '"';
+        if ($this->comment) {
+            $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+        }
+        if ($this->previous) {
+            $o.= ' PREVIOUS="' . $this->previous . '"';
+        }
+        if ($this->next) {
+            $o.= ' NEXT="' . $this->next . '"';
+        }
+            $o.= '>' . "\n";
+    /// Now the fields
+        if ($this->fields) {
+            $o.= '      <FIELDS>' . "\n";
+            foreach ($this->fields as $field) {
+                $o.= $field->xmlOutput();
+            }
+            $o.= '      </FIELDS>' . "\n";
+        }
+    /// Now the keys
+        if ($this->keys) {
+            $o.= '      <KEYS>' . "\n";
+            foreach ($this->keys as $key) {
+                $o.= $key->xmlOutput();
+            }
+            $o.= '      </KEYS>' . "\n";
+        }
+    /// Now the indexes
+        if ($this->indexes) {
+            $o.= '      <INDEXES>' . "\n";
+            foreach ($this->indexes as $index) {
+                $o.= $index->xmlOutput();
+            }
+            $o.= '      </INDEXES>' . "\n";
+        }
+        $o.= '    </TABLE>' . "\n";
+
+        return $o;
+    }
+
+    /** 
+     * This function will return all the errors found in one table
+     * looking recursively inside each field/key/index. Returns
+     * an array of errors or false
+     */
+    function getAllErrors() {
+
+        $errors = array();
+    /// First the table itself
+        if ($this->getError()) {
+            $errors[] = $this->getError();
+        }
+    /// Delegate to fields
+        if ($fields = $this->getFields()) {
+            foreach ($fields as $field) {
+                if ($field->getError()) {
+                    $errors[] = $field->getError();
+                }
+            }
+        }
+    /// Delegate to keys
+        if ($keys = $this->getKeys()) {
+            foreach ($keys as $key) {
+                if ($key->getError()) {
+                    $errors[] = $key->getError();
+                }
+            }
+        }
+    /// Delegate to indexes
+        if ($indexes = $this->getIndexes()) {
+            foreach ($indexes as $index) {
+                if ($index->getError()) {
+                    $errors[] = $index->getError();
+                }
+            }
+        }
+    /// Return decision
+        if (count($errors)) {
+            return $errors;
+        } else {
+            return false;
+        }
+    }
+
+    /** 
+     * This function will return the SQL code needed to create the table for the specified DB and
+     * prefix. Just one simple wrapper over generators.
+     */
+    function getCreateTableSQL ($dbtype, $prefix) {
+        $classname = 'XMLDB' . $dbtype;
+        $generator = new $classname();
+        $generator->setPrefix($prefix);
+        return $generator->getCreateTableSQL($this);
+    }
+}
+
+?>
diff --git a/lib/xmldb/classes/generators/mysql/mysql.class.php b/lib/xmldb/classes/generators/mysql/mysql.class.php
new file mode 100644 (file)
index 0000000..fe1074f
--- /dev/null
@@ -0,0 +1,228 @@
+<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 2001-3001 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 the base generator class where all the
+/// needed functions to generate proper SQL are defined. 
+
+/// If fact, this class generate SQL code to be used against MySQL
+/// so the rest of classes will inherit, by default, the same logic.
+/// Functions will be overriden as needed to generate correct SQL.
+
+class XMLDBmysql {
+
+    var $quote_string = '`';   // String used to quote names
+
+    var $primary_keys = true;  // Does the constructor build primary keys
+    var $unique_keys = false;  // Does the constructor build unique keys
+    var $foreign_keys = false; // Does the constructor build foreign keys
+
+    var $primary_index = false;// Does the constructor need to build one index for primary keys
+    var $unique_index = true;  // Does the constructor need to build one index for unique keys
+    var $foreign_index = true; // Does the constructor need to build one index for foreign keys
+
+    var $prefix;  // Prefix to be used for all the DB objects
+
+    /**
+     * Creates one new XMLDBmysql
+     */
+    function XMLDBmysql() {
+        global $CFG;
+        $this->prefix = '';
+    }
+
+    /**
+     * Set the prefix
+     */
+    function setPrefix($prefix) {
+        $this->prefix = $prefix;
+    }
+
+    /**
+     * Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
+     */
+    function getType ($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
+
+        switch ($xmldb_type) {
+            case XMLDB_TYPE_INTEGER:    // From http://mysql.com/doc/refman/5.0/en/numeric-types.html!
+                if (empty($xmldb_length)) {
+                    $xmldb_length = 10;
+                }
+                if ($xmldb_length > 9) {
+                    $dbtype = 'BIGINT';
+                    $xmldb_length = 10;
+                } else if ($xmldb_length > 6) {
+                    $dbtype = 'INT';
+                } else if ($xmldb_length > 4) {
+                    $dbtype = 'MEDIUMINT';
+                } else if ($xmldb_length > 2) {
+                    $dbtype = 'SMALLINT';
+                } else {
+                    $dbtype = 'TINTINT';
+                }
+                $dbtype .= '(' . $xmldb_length . ')';
+                break;
+            case XMLDB_TYPE_NUMBER:
+                $dbtype = 'DECIMAL';
+                if (!empty($xmldb_length)) {
+                    $dbtype .= '(' . $xmldb_length;
+                    if (!empty($xmldb_decimals)) {
+                        $dbtype .= ',' . $xmldb_decimals;
+                    }
+                    $dbtype .= ')';
+                }
+                break;
+            case XMLDB_TYPE_FLOAT:
+                $dbtype = 'FLOAT';
+                if (!empty($xmldb_length)) {
+                    $dbtype .= '(' . $xmldb_length;
+                    if (!empty($xmldb_decimals)) {
+                        $dbtype .= ',' . $xmldb_decimals;
+                    }
+                    $dbtype .= ')';
+                }
+                break;
+            case XMLDB_TYPE_CHAR:
+                $dbtype = 'VARCHAR';
+                if (!empty($xmldb_length)) {
+                    $xmldb_length='255';
+                }
+                $dbtype .= '(' . $xmldb_length . ')';
+                break;
+            case XMLDB_TYPE_TEXT:
+                if (empty($xmldb_length)) {
+                    $xmldb_length = 'small';
+                }
+                if ($xmldb_length = 'small') {
+                    $dbtype = 'TEXT';
+                } else if ($xmldb_length = 'medium') {
+                    $dbtype = 'MEDIUMTEXT';
+                } else {
+                    $dbtype = 'BIGTEXT';
+                }
+                break;
+            case XMLDB_TYPE_BINARY:
+                if (empty($xmldb_length)) {
+                    $xmldb_length = 'small';
+                }
+                if ($xmldb_length = 'small') {
+                    $dbtype = 'BLOB';
+                } else if ($xmldb_length = 'medium') {
+                    $dbtype = 'MEDIUMBLOB';
+                } else {
+                    $dbtype = 'BIGBLOB';
+                }
+                break;
+            case XMLDB_TYPE_DATETIME:
+                $dbtype = 'DATETIME';
+        }
+        return $dbtype;
+    }
+
+    /**
+     * Given one correct XMLDBTable, returns the complete SQL lines to create it
+     */
+    function getCreateTableSQL($xmldb_table) {
+
+    /// Table header
+        $table = 'CREATE TABLE ' . $this->getEncQuoted($this->prefix . $xmldb_table->getName()) . ' (';
+
+        if (!$xmldb_fields = $xmldb_table->getFields()) {
+            return false;
+        }
+    /// Add the fields, separated by commas
+        foreach ($xmldb_fields as $xmldb_field) {
+            $table .= "\n    " . $this->getCreateFieldSQL($xmldb_field) . ',';
+        }
+    /// Table footer, trim the latest comma
+        $table = trim($table,',');
+        $table .= "\n)";
+        if ($xmldb_table->getComment()) {
+            $table .= " COMMENT='" . $xmldb_table->getComment() . "';\n\n";
+        }
+        return $table;
+    }
+
+    /**
+     * Given one correct XMLDBField, returns the complete SQL line to create it
+     */
+    function getCreateFieldSQL($xmldb_field) {
+
+    /// The name
+        $field = $this->getEncQuoted($xmldb_field->getName());
+    /// The type and length (if the field isn't enum)
+        if (!$xmldb_field->getEnum()) {
+            $field .= ' ' . $this->getType($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
+        } else {
+        /// If enum, do it with its values
+            $field .= ' enum(' . implode(', ', $xmldb_field->getEnumValues()) . ')';
+        }
+    /// The unsigned
+        if ($xmldb_field->getType() == XMLDB_TYPE_INTEGER ||
+            $xmldb_field->getType() == XMLDB_TYPE_NUMBER ||
+            $xmldb_field->getType() == XMLDB_TYPE_FLOAT) {
+            if ($xmldb_field->getUnsigned()) {
+                $field .= ' unsigned';
+            }
+        }
+    /// The not null
+        if ($xmldb_field->getNotNull()) {
+            $field .= ' NOT NULL';
+        }
+    /// The sequence
+        if ($xmldb_field->getSequence()) {
+            $field .= ' auto_increment';
+        }
+    /// The default
+        if ($xmldb_field->getDefault() != NULL) {
+            $field .= ' default ';
+            if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
+                $xmldb_field->getType() == XMLDB_TYPE_TEXT) {
+                    $field .= "'" . $xmldb_field->getDefault() . "'";
+            } else {
+                $field .= $xmldb_field->getDefault();
+            }
+        } else {
+        /// We force default '' for not null char columns without proper default
+        /// some day this should be out!
+            if ($xmldb_field->getType() == XMLDB_TYPE_CHAR &&
+                $xmldb_field->getNotNull()) {
+                $field .= ' default ' . "''";
+            }
+        }
+        return $field;
+    }
+
+    /**
+     * Given any string, enclose it by the proper quotes
+     */
+    function getEncQuoted($string) {
+        return $this->quote_string . $string . $this->quote_string;
+    }
+
+}
+
+?>
diff --git a/lib/xmldb/xmldb.dtd b/lib/xmldb/xmldb.dtd
new file mode 100644 (file)
index 0000000..2a5907e
--- /dev/null
@@ -0,0 +1,62 @@
+<!ELEMENT FIELD EMPTY >
+<!ATTLIST FIELD DECIMALS NMTOKEN #IMPLIED >
+<!ATTLIST FIELD DEFAULT NMTOKEN #IMPLIED >
+<!ATTLIST FIELD ENUM ( false | true ) #REQUIRED >
+<!ATTLIST FIELD ENUMVALUES CDATA #IMPLIED >
+<!ATTLIST FIELD LENGTH NMTOKEN #REQUIRED >
+<!ATTLIST FIELD NAME NMTOKEN #REQUIRED >
+<!ATTLIST FIELD NEXT NMTOKEN #IMPLIED >
+<!ATTLIST FIELD NOTNULL ( false | true ) #REQUIRED >
+<!ATTLIST FIELD PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST FIELD SEQUENCE ( false | true ) #REQUIRED >
+<!ATTLIST FIELD TYPE ( binary | char | float | int | number | text ) #REQUIRED >
+<!ATTLIST FIELD UNSIGNED ( false | true ) #IMPLIED >
+
+<!ELEMENT FIELDS ( FIELD+ ) >
+
+<!ELEMENT INDEX EMPTY >
+<!ATTLIST INDEX COMMENT CDATA #IMPLIED >
+<!ATTLIST INDEX FIELDS CDATA #REQUIRED >
+<!ATTLIST INDEX NAME NMTOKEN #REQUIRED >
+<!ATTLIST INDEX NEXT NMTOKEN #IMPLIED >
+<!ATTLIST INDEX PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST INDEX UNIQUE ( false | true ) #REQUIRED >
+
+<!ELEMENT INDEXES ( INDEX+ ) >
+
+<!ELEMENT KEY EMPTY >
+<!ATTLIST KEY COMMENT CDATA #IMPLIED >
+<!ATTLIST KEY FIELDS CDATA #REQUIRED >
+<!ATTLIST KEY NAME NMTOKEN #REQUIRED >
+<!ATTLIST KEY NEXT NMTOKEN #IMPLIED >
+<!ATTLIST KEY PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST KEY TYPE ( primary | unique | foreign ) #REQUIRED >
+
+<!ELEMENT KEYS ( KEY+ ) >
+
+<!ELEMENT SENTENCE EMPTY >
+<!ATTLIST SENTENCE TEXT CDATA #REQUIRED >
+
+<!ELEMENT SENTENCES ( SENTENCE+ ) >
+
+<!ELEMENT STATEMENT ( SENTENCES ) >
+<!ATTLIST STATEMENT COMMENT CDATA #REQUIRED >
+<!ATTLIST STATEMENT NAME CDATA #REQUIRED >
+<!ATTLIST STATEMENT TABLE NMTOKEN #REQUIRED >
+<!ATTLIST STATEMENT TYPE NMTOKEN #REQUIRED >
+
+<!ELEMENT STATEMENTS ( STATEMENT ) >
+
+<!ELEMENT TABLE ( FIELDS, KEYS, INDEXES? ) >
+<!ATTLIST TABLE COMMENT CDATA #REQUIRED >
+<!ATTLIST TABLE NAME ID #REQUIRED >
+<!ATTLIST TABLE NEXT NMTOKEN #IMPLIED >
+<!ATTLIST TABLE PREVIOUS NMTOKEN #IMPLIED >
+
+<!ELEMENT TABLES ( TABLE+ ) >
+
+<!ELEMENT XMLDB ( TABLES, STATEMENTS ) >
+<!ATTLIST XMLDB COMMENT CDATA #REQUIRED >
+<!ATTLIST XMLDB PATH CDATA #REQUIRED >
+<!ATTLIST XMLDB VERSION NMTOKEN #REQUIRED >
+
diff --git a/lib/xmldb/xmldb.xsd b/lib/xmldb/xmldb.xsd
new file mode 100644 (file)
index 0000000..ace9053
--- /dev/null
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <xs:element name="FIELD">
+    <xs:complexType>
+      <xs:attribute name="ENUM" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="false" />
+            <xs:enumeration value="true" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="LENGTH" type="xs:NMTOKEN" use="required" />
+      <xs:attribute name="DECIMALS" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="SEQUENCE" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="false" />
+            <xs:enumeration value="true" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="ENUMVALUES" type="xs:string" use="optional" />
+      <xs:attribute name="DEFAULT" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="TYPE" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="binary" />
+            <xs:enumeration value="char" />
+            <xs:enumeration value="float" />
+            <xs:enumeration value="int" />
+            <xs:enumeration value="number" />
+            <xs:enumeration value="text" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="UNSIGNED" use="optional">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="false" />
+            <xs:enumeration value="true" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="NOTNULL" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="false" />
+            <xs:enumeration value="true" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="NAME" type="xs:NMTOKEN" use="required" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="FIELDS">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="FIELD" maxOccurs="unbounded" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="INDEX">
+    <xs:complexType>
+      <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="COMMENT" type="xs:string" use="optional" />
+      <xs:attribute name="UNIQUE" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="false" />
+            <xs:enumeration value="true" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="NAME" type="xs:NMTOKEN" use="required" />
+      <xs:attribute name="FIELDS" type="xs:string" use="required" />
+      <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="INDEXES">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="INDEX" maxOccurs="unbounded" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="KEY">
+    <xs:complexType>
+      <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="COMMENT" type="xs:string" use="optional" />
+      <xs:attribute name="NAME" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="name" />
+            <xs:enumeration value="plugin_name" />
+            <xs:enumeration value="primary" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="FIELDS" type="xs:string" use="required" />
+      <xs:attribute name="TYPE" use="required">
+        <xs:simpleType>
+          <xs:restriction base="xs:NMTOKEN">
+            <xs:enumeration value="primary" />
+            <xs:enumeration value="unique" />
+            <xs:enumeration value="foreign" />
+          </xs:restriction>
+        </xs:simpleType>
+      </xs:attribute>
+      <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="KEYS">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="KEY" maxOccurs="unbounded" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="SENTENCE">
+    <xs:complexType>
+      <xs:attribute name="TEXT" type="xs:string" use="required" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="SENTENCES">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="SENTENCE" maxOccurs="unbounded" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="STATEMENT">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="SENTENCES" />
+      </xs:sequence>
+      <xs:attribute name="COMMENT" type="xs:string" use="required" />
+      <xs:attribute name="TABLE" type="xs:NMTOKEN" use="required" />
+      <xs:attribute name="NAME" type="xs:string" use="required" />
+      <xs:attribute name="TYPE" type="xs:NMTOKEN" use="required" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="STATEMENTS">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="STATEMENT" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="TABLE">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="FIELDS" />
+        <xs:element ref="KEYS" />
+        <xs:element ref="INDEXES" minOccurs="0" />
+      </xs:sequence>
+      <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+      <xs:attribute name="COMMENT" type="xs:string" use="required" />
+      <xs:attribute name="NAME" type="xs:ID" use="required" />
+      <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="TABLES">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="TABLE" maxOccurs="unbounded" />
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+  <xs:element name="XMLDB">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="TABLES" />
+        <xs:element ref="STATEMENTS" />
+      </xs:sequence>
+      <xs:attribute name="PATH" type="xs:string" use="required" />
+      <xs:attribute name="COMMENT" type="xs:string" use="required" />
+      <xs:attribute name="VERSION" type="xs:NMTOKEN" use="required" />
+    </xs:complexType>
+  </xs:element>
+
+</xs:schema>
\ No newline at end of file