<?php
/*------------------------------------------------------------------------------
Copyright (c) 2004, 2005 thaler

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

$Id$

You may retrieve the latest version of this file at the tsWebEditor home page,
located at http://tswebeditor.tigris.org

Known Issues:
------------------------------------------------------------------------------*/

class Properties {
  private $arrlist = array();
  
  function set($name, $value, $save = true) {
    $this->arrlist[$name] = array('value' => $value, 'save' => $save);
  }

  function get($name, $default = null) {
    if(isset($this->arrlist[$name])) {
      return $this->arrlist[$name]['value'];
    } else
      return $default;
  }
  
  function Save(DOMElement $xml) {
    if(count($this->arrlist) == 0) return;
    
    $props = $xml->appendChild(new DOMElement('properties'));

    foreach($this->arrlist as $name => $x) {
      if($x['save']) {
        $prop = $props->appendChild(new DOMElement('property'));
        
        $prop->setAttribute('name', $name);
        $prop->setAttribute('value', $x['value']);
      }
    }
  }

  function Load(DOMElement $xml) {
    $tmp = $xml->getElementsByTagName('properties');
    if($tmp->length > 0) {
      foreach($tmp->item(0)->childNodes as $prop) {
        $this->set($prop->getAttribute('name'), $prop->getAttribute('value'));
      }
    }
  }
}

abstract class AbstractDummy {
  protected $FName = '';
  public $Comment = '';
  private $Props = null;
  
  function __construct() {
    $this->Props = new Properties();
  }
  
  protected abstract function setName($Value);

  function getName() {
    return $this->FName;
  }
  
  function getComment() {
    return $this->Comment;
  }
  
  function setComment($Value) {
    $this->Comment = $Value;
  }
  
  function getTypeName() {
    return get_class($this);
  }
  
  function getProps() {
    return $this->Props;
  }

  abstract function SaveToXml(DOMElement $xml);
  abstract function LoadFromXml(DOMElement $xml);
  
  function getXMLValue(DOMElement $xml, $name) {
    $list = $xml->getElementsByTagName($name);
    if($list->length > 0) {
      return $list->item(0)->nodeValue;
    } else
      return '';
  }
  
  function WriteCData(DOMElement $xml, $name, $value) {
    $e = new DOMElement($name);
    $cdata = new DOMCdataSection($value);
    $xml->appendChild($e);
    $e->appendChild($cdata);
  }
}

abstract class AbstractObjList {
  private $FList;

  function __construct() {
    $this->FList = array();
  }

  function __destruct() {
    //FList.Free;
    //parent::__destruct();
  }
  
  function freeName($Name, $NoNumberCheck = false) {
    if($NoNumberCheck) {
      if($this->findObj($Name) == null)
        return $Name;
    }

    $tmp = 1;
    $Result = '';
    while(true) {
      $Result = $Name . $tmp;
      if($this->findObj($Result) == null)
        break;
      else
        $tmp ++;
    }
    
    return $Result;
  }

  function Add(AbstractDummy $Obj) {
    $this->FList[] = $Obj;
  }

  function Clear() {
    $this->FList = array();
  }

  function Delete(AbstractDummy $Obj) {
    for($i = 0; $i < count($this->FList); $i++) {
      if($this->FList[$i] === $Obj) {
        array_splice($this->FList, $i, 1);
        break;
      }
    }
  }

  function findObj($Name) {
    for($i = 0; $i < count($this->FList); $i++) {
      if(strtolower($this->FList[$i]->getName()) == strtolower($Name))
        return $this->FList[$i];
    }

    return null;
  }

  function getCount() {
    return count($this->FList);
  }

  function getObj($Index) {
    return $this->FList[$Index];
  }
}

class ObjList extends AbstractObjList {

  function Filter($filter, $offset = -1) {
    for($i = $offset + 1; $i < $this->getCount(); $i++) {
      if($this->getObj($i) instanceof $filter) {
        return $i;
      }
    }
    
    return -1;
  }


  function SaveToXml(DOMNode $xml) {
    $ts = $xml->appendChild(new DOMElement('tables'));
    $vs = $xml->appendChild(new DOMElement('views'));
    $se = $xml->appendChild(new DOMElement('sequences'));
    $ix = $xml->appendChild(new DOMElement('indexes'));
    $rf = $xml->appendChild(new DOMElement('references'));
    $pk = $xml->appendChild(new DOMElement('primarykeys'));
    $uq = $xml->appendChild(new DOMElement('uniques'));
    $ch = $xml->appendChild(new DOMElement('checks'));
    $tr = $xml->appendChild(new DOMElement('triggers'));
    $sp = $xml->appendChild(new DOMElement('storedprocs'));

    for($i = 0; $i < $this->getCount(); $i++) {
      if($this->getObj($i) instanceof Table)
        $this->getObj($i)->SaveToXml($ts);
      else if($this->getObj($i) instanceof View)
        $this->getObj($i)->SaveToXml($vs);
      else if($this->getObj($i) instanceof Sequence)
        $this->getObj($i)->SaveToXml($se);
      else if($this->getObj($i) instanceof Reference)
        $this->getObj($i)->SaveToXml($rf);
      else if($this->getObj($i) instanceof ConstPrimaryKey)
        $this->getObj($i)->SaveToXml($pk);
      else if($this->getObj($i) instanceof ConstUnique)
        $this->getObj($i)->SaveToXml($uq);
      else if($this->getObj($i) instanceof ConstCheck)
        $this->getObj($i)->SaveToXml($ch);
      else if($this->getObj($i) instanceof Index)
        $this->getObj($i)->SaveToXml($ix);
      else if($this->getObj($i) instanceof Trigger)
        $this->getObj($i)->SaveToXml($tr);
      else if($this->getObj($i) instanceof StoredProc)
        $this->getObj($i)->SaveToXml($sp);
    }

  }

  function LoadFromXml(DOMNode $xml) {
    $cnt = 0;
    
    $tmpList = $xml->getElementsByTagName('tables');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new Table($this, '--tmp' . $obj->nodeName . $cnt++ .  '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('indexes');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new Index($this, null, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('references');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new Reference($this, null, null, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('primarykeys');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new ConstPrimaryKey($this, null, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('uniques');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new ConstUnique($this, null, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('views');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new View($this, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('sequences');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new Sequence($this, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('checks');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new ConstCheck($this, null, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }
    
    $tmpList = $xml->getElementsByTagName('triggers');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new Trigger($this, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }

    $tmpList = $xml->getElementsByTagName('storedprocs');
    if($tmpList->length > 0) {
      foreach($tmpList->item(0)->childNodes as $obj) {
        $tmp = new StoredProc($this, '--tmp' . $obj->nodeName . $cnt++ . '--');
        $tmp->LoadFromXml($obj);
      }
    }
  }
}

abstract class AbstractSQLObj extends AbstractDummy {
  private $FParent = null;
  
  function __construct(ObjList $Parent, $Name) {
    parent::__construct();

    $this->setParentAndName($Parent, $Name);
  }

  function __destruct() {
    if($this->FParent !== null)  // remove from old parent
      $this->FParent->Delete($this);

    //$this->FParent = null;
  }

  function setName($Value) {
    if($this->getName() != $Value)
    {
      if($this->getParent()->findObj($Value) == null) {
        $this->FName = $Value;
      } else {
        throw new Exception('Name ' . $Value . ' already in use !');
      }
    }
  }
  
  function getParent() {
    return $this->FParent;
  }

  function setParent($Value) {
    if($this->FParent !== $Value) {
      if($this->FParent !== null)  // remove from old parent
        $this->FParent->Delete($this);

      $this->FParent = $Value;

      if ($this->FParent !== null)  // add to new parent
        $this->FParent->Add($this);
    }
  }

  private function setParentAndName(ObjList $Parent, $Name) {
    $this->setParent($Parent);
    $this->setName($Name);
  }
  
  abstract protected function DoSaveXml(DOMElement $xml);
  abstract protected function DoLoadXml(DOMElement $xml);
  
  function LoadFromXml(DOMElement $xml) {
    $this->setName($this->getXMLValue($xml, 'name'));
    $this->setComment($this->getXMLValue($xml, 'comment'));
    
    $this->DoLoadXml($xml);
    $this->getProps()->Load($xml);
  }

  function SaveToXml(DOMElement $xml) {
    $node = $xml->appendChild(new DOMElement(strtolower(get_class($this))));

    $node->appendChild(new DOMElement('name', $this->getName()));
    //$node->appendChild(new DOMElement('comment', $this->Comment));
    $this->WriteCData($node, 'comment', $this->Comment);
    
    $this->DoSaveXml($node);

    $this->getProps()->Save($node);
  }
}

abstract class AbstractRefObj extends AbstractSQLObj {
  abstract function CheckCol(Table $Table, $ColumnName);
  abstract function Rename(Table $Table, $OldName, $NewName);
}

class Column extends AbstractDummy {
  private $FParent = null;
  public $DataType = '';
  public $Size = '';
  public $DefaultValue = '';
  public $NotNull = false;
  public $PrimaryKey = false;

  function __construct(Table $Parent, $ColName) {
    parent::__construct();

    $this->FParent = $Parent;
    $this->setName($ColName);

    $this->FParent->getColList()->AddCol($this);
  }
  
  function getDataTypeSize() {
    return $this->DataType . (empty($this->Size) ? '' : '(' . $this->Size . ')');
  }

  function SaveToXml(DOMElement $xml) {
    $col = $xml->appendChild(new DOMElement('col'));
    
    $col->appendChild(new DOMElement('name', $this->getName()));
    $col->appendChild(new DOMElement('comment', $this->Comment));
    $col->appendChild(new DOMElement('datatype', $this->DataType));
    $col->appendChild(new DOMElement('size', $this->Size));
    $col->appendChild(new DOMElement('defaultvalue', $this->DefaultValue));
    $col->appendChild(new DOMElement('notnull', $this->NotNull));
//    $col->appendChild(new DOMElement('check', $this->Check));
    $col->appendChild(new DOMElement('primarykey', $this->PrimaryKey));
//    $col->appendChild(new DOMElement('index', $this->Index));
//    $col->appendChild(new DOMElement('unique', $this->Unique));

    $this->getProps()->Save($col);
  }

  function LoadFromXml(DOMElement $xml) {
    $this->setName($this->getXMLValue($xml, 'name'));
    $this->setComment($this->getXMLValue($xml, 'comment'));
    $this->DataType = $this->getXMLValue($xml, 'datatype');
    $this->Size = $this->getXMLValue($xml, 'size');
    $this->DefaultValue = $this->getXMLValue($xml, 'defaultvalue');
    $this->NotNull = ($this->getXMLValue($xml, 'notnull') == '-1');
    $this->PrimaryKey = ($this->getXMLValue($xml, 'primarykey') == '-1');
    
//    $this->FCheck = $this->getXMLValue($xml, 'check');
//    $this->FIndex = ($this->getXMLValue($xml, 'index') == '-1');
//    $this->FUnique = ($this->getXMLValue($xml, 'unique') == '-1');

    $this->getProps()->Load($xml);
  }

  function setName($Value) {
    if($Value != $this->getName()) {
      if($this->getParent()->getColList()->findCol($Value) == null)
        $this->FName = $Value;
      else
        throw new Exception('Column Name ' . $Value . ' already in use !');
    }
  }

  function getParent() {
    return $this->FParent;
  }
}

class ColList extends AbstractObjList {

  function AddCol(Column $Col) {
    $this->Add($Col);
  }

  function findCol($Name) {
    for($i = 0; $i < $this->getCount(); $i++) {
      if($this->getCol($i)->getName() == $Name) {
        return $this->getCol($i);
      }
    }
  }

  function getCol($Index) {
    return $this->getObj($Index);
  }

  function SaveToXml(DOMElement $xml) {
    $cols = $xml->appendChild(new DOMElement('cols'));

    for($i = 0; $i < $this->getCount(); $i++) {
      $this->getObj($i)->SaveToXml($cols);
    }
  }

  function LoadFromXml(Table $Parent, DOMElement $xml) {
    $tmp = $xml->getElementsByTagName('cols');
    if($tmp->length > 0) {
      foreach($tmp->item(0)->childNodes as $col) {
        $Parent->AddColumn('')->LoadFromXml($col);
      }
    }
  }
}

class Table extends AbstractSQLObj {
  private $FColList;
  private $FRefList;

  function __construct(ObjList $Parent, $Name) {
    parent::__construct($Parent, $Name);

    $this->FColList = new ColList();
    $this->FRefList = new ObjList();
  }

  function __destruct() {
    //FColList.Free;
    //FRefList.Free;

    parent::__destruct();
  }
  
  function getColList() {
    return $this->FColList;
  }

  function AddColumn($Name) {
    if($Name == '')
      $FName = $this->getColList()->freeName('Col');
    else
      $FName = $Name;

    return new Column($this, $FName);
  }

  function RefAdd(AbstractRefObj $Ref) {
    $this->FRefList->Add($Ref);
  }

  function RefDel(AbstractRefObj $Ref) {
    $this->FRefList->Delete($Ref);
  }

  function RefCheck($ColName) {
    for($i = 0; $i < count($this->FFRefList); $i++) {
      if($this->FRefList[$i]->CheckCol($this, $ColName)) {
        return true;
      }
    }
    
    return false;
  }

  function RefRenameCol($OldName, $NewName) {
    for($i = 0; $i < count($this->FFRefList); $i++) {
      $this->FRefList[$i]->Rename($this, $OldName, $NewName);
    }
  }

  function getRef($Index) {
    return $this->FRefList->getObj($Index);
  }

  function getRefListCount(){
    return $this->FRefList->getCount();
  }

  function DoLoadXml(DOMElement $xml) {
    $this->FColList->LoadFromXml($this, $xml);
  }

  function DoSaveXml(DOMElement $xml) {
    $this->FColList->SaveToXml($xml);
  }

  function UpdatePrimaryKey() {
    $pk = null;

    for($i = 0; $i < $this->getRefListCount(); $i++) {
      if(($this->getRef($i) instanceof ConstPrimaryKey)) {
        $pk = $this->getRef($i);
        break;
      }
    }

    $y = false;
    
    for($i = 0; $i < $this->FColList->getCount(); $i++) {
      if($this->FColList->getCol($i)->PrimaryKey) {
        $y = true;
        break;
      }
    }

    if(!$y && $pk !== null) // no primary keys in FColList, but we found a Primary Key Constraint
      $pk->ClearColNames();
    else if($y) {
      if($pk === null)
        $pk = new ConstPrimaryKey($this->getParent(), $this, $this->getParent()->freeName('pk_' . $this->getName(), true));

      $pk->ClearColNames();

      for($i = 0; $i < $this->FColList->getCount(); $i++) {
        if($this->FColList->getCol($i)->PrimaryKey) {
          $pk->AddColName($this->FColList->getCol($i)->getName());
        }
      }
    }
  }
}

define('coNoOption', 0);
define('coCascade', 1);
define('coLocal', 2);

class View extends AbstractSQLObj {
  public $SQL = '';
  public $WithCheckOption = false;
  public $WithCheckOptionType = 0;
  
  function DoLoadXml(DOMElement $xml) {
    $this->SQL = $this->getXMLValue($xml, 'sql');
    $this->WithCheckOption = ($this->getXMLValue($xml, 'withcheckoption') == '-1');
    $this->WithCheckOptionType = $this->getXMLValue($xml, 'withcheckoptiontype');
  }

  function DoSaveXml(DOMElement $xml) {
    $this->WriteCData($xml, 'sql', $this->SQL);
    $xml->appendChild(new DOMElement('withcheckoption', $this->WithCheckOption));
    $xml->appendChild(new DOMElement('withcheckoptiontype', $this->WithCheckOptionType));
  }
}

class Sequence extends AbstractSQLObj {
  public $Increment = '';
  public $MinValue = '';
  public $MaxValue = '';
  public $Start = '';
  public $Cache = '';

  function DoLoadXml(DOMElement $xml) {
    $this->Increment = $this->getXMLValue($xml, 'increment');
    $this->MinValue = $this->getXMLValue($xml, 'minvalue');
    $this->MaxValue = $this->getXMLValue($xml, 'maxvalue');
    $this->Start = $this->getXMLValue($xml, 'start');
    $this->Cache = $this->getXMLValue($xml, 'cache');
  }

  function DoSaveXml(DOMElement $xml) {
    $xml->appendChild(new DOMElement('increment', $this->Increment));
    $xml->appendChild(new DOMElement('minvalue', $this->MinValue));
    $xml->appendChild(new DOMElement('maxvalue', $this->MaxValue));
    $xml->appendChild(new DOMElement('start', $this->Start));
    $xml->appendChild(new DOMElement('cache', $this->Cache));
  }
}

abstract class AbstractConstraint extends AbstractRefObj {
  private $FTable;
  private $FColNames;
  
  function __construct(ObjList $Parent, $Table, $ConstName) {
    parent::__construct($Parent, $ConstName);

    $this->FColNames = array();
    $this->setTable($Table);
  }

  function __destruct() {
    $this->setTable(null);

    parent::__destruct();
  }
  
  function getColNames() {
    return $this->FColNames;
  }
  
  function ClearColNames() {
    $this->FColNames = array();
  }
  
  function AddColName($Name) {
    $this->FColNames[] = $Name;
  }

  
  function CheckCol(Table $Table, $ColumnName) { // todo: test
    return in_array($ColumnName, $this->FColNames);
  }
  
  function getColString() {
    $r = '';
    for($i = 0; $i < count($this->FColNames); $i++)
      $r .= ($i > 0 ? ', ' : '') . $this->FColNames[$i];

    return $r;
  }

  function DoLoadXml(DOMElement $xml) {
    $table = $this->getXMLValue($xml, 'table');
    if($table != '') {
      $tbl = $this->getParent()->findObj($table);
      $this->setTable($tbl);
      
      if(($tbl !== null) && ($tbl instanceof Table)) {
        $tmp = $xml->getElementsByTagName('cols');
        if($tmp->length > 0) {
          foreach($tmp->item(0)->childNodes as $col) {
            $this->AddColName($col->nodeValue);
            //$c = $tbl->getColList()->findCol($col->nodeValue);
            //if($c !== null) {
            //  $this->AddColName($c->getName());
            //}
          }
        }
      }
    }
    
    if($this->getTable() == null) {
      print 'Table for Constraint ' + $this->getName() + ' not found ! I will remove this Constraint.';
      $this->__destruct();
      //$this = null;
    }
  }
  
  function DoSaveXml(DOMElement $xml) {
    $xml->appendChild(new DOMElement('table', $this->FTable->getName()));

    $cols = $xml->appendChild(new DOMElement('cols'));

    for($i = 0; $i < count($this->FColNames); $i++) {
      $cols->appendChild(new DOMElement('col', $this->FColNames[$i]));
    }
  }

  function Rename(Table $Table, $OldName, $NewName) {
    for($i = 0; $i < count($this->FColNames); $i++) {
      if($this->FColNames[$i] == $OldName) {
        $this->FColNames[$i] = $NewName;
        break;
      }
    }
  }

  function getTable() {
    return $this->FTable;
  }

  function setTable($Value) {
    if($this->FTable !== $Value) {
      if($this->FTable !== null)  // remove from old table
        $this->FTable->RefDel($this);

      $this->FTable = $Value;

      if($this->FTable !== null)  // add to new table
        $this->FTable->RefAdd($this);
    }
  }
}

abstract class Constraint extends AbstractConstraint {}

class Index extends AbstractConstraint {
  public $IndexKind = '';
  public $IndexType = '';
  
  function DoLoadXml(DOMElement $xml) {
    parent::DoLoadXml($xml);

    $this->IndexKind = $this->getXMLValue($xml, 'kind');
    $this->IndexType = $this->getXMLValue($xml, 'type');
  }

  function DoSaveXml(DOMElement $xml) {
    parent::DoSaveXml($xml);

    $xml->appendChild(new DOMElement('kind', $this->IndexKind));
    $xml->appendChild(new DOMElement('type', $this->IndexType));
  }
}

class ConstPrimaryKey extends Constraint {

  function setTable($Value) {
    parent::setTable($Value);

    if($this->getTable() !== null) {
      for($i = 0; $i < $this->getTable()->getRefListCount(); $i++) {
        if(($this->getTable()->getRef($i) instanceof ConstPrimaryKey) && ($this->getTable()->getRef($i) !== $this)) {
          print 'Table ' + $this->getTable()->getName() + ' has more than one primary keys !';
          break;
        }
      }
    }
  }

  function UpdateTable() {
    if($this->getTable() === null) return;
    
    for($i = 0; $i < $this->getTable()->getColList()->getCount(); $i++) {
      $this->getTable()->getColList()->getCol($i)->PrimaryKey =
        $this->checkCol($this->getTable(), $this->getTable()->getColList()->getCol($i)->getName());
    }
  }
}

class ConstUnique extends Constraint {}

class ConstCheck extends Constraint {
  public $SQL = '';
  
  function getColString() {
    return $this->SQL;
  }

  function DoSaveXml(DOMElement $xml) {
    $xml->appendChild(new DOMElement('table', $this->getTable()->getName()));
    $this->WriteCData($xml, 'sql', $this->SQL);
  }
  
  function DoLoadXml(DOMElement $xml) {
    parent::DoLoadXml($xml);
    
    $this->SQL = $this->getXMLValue($xml, 'sql');
  }
}

define('raNoAction', 0);
define('raRestrict', 1);
define('raCascade', 2);
define('raSetNull', 3);
define('raSetDefault', 4);

define('rmNoMatchType', 0);
define('rmMatchFull', 1);
define('rmMatchPartial', 2);
define('rmMatchSimple', 3);


class Reference extends AbstractRefObj {
  private $FTableSource;
  private $FTableDest;
  private $FColSource;
  private $FColDest;
  public $OnUpdate;
  public $OnDelete;
  public $MatchType;
  

  function __construct(ObjList $Parent, $TableSource, $TableDest, $ReferenceName) {
    parent::__construct($Parent, $ReferenceName);

    $this->FColSource = array();
    $this->FColDest = array();

    $this->setTableSource($TableSource);
    $this->setTableDest($TableDest);
  }

  function __destruct() {
    $this->setTableSource(null);
    $this->setTableDest(null);

    parent::__destruct();
  }

  function CheckCol(Table $Table, $ColumnName) {    //todo: test
    if($Table === $this->FTableSource)
      return in_array($ColumnName, $this->FColSource);
    else if($Table === $this->FTableDest)
      return in_array($ColumnName, $this->FColDest);

    return false;
  }

  function Rename(Table $Table, $OldName, $NewName) {
    if($Table == $this->FTableSource) {
      for($i = 0; $i < count($this->FColDest); $i++) {
        if($this->FColDest[$i] == $OldName) {
          $this->FColDest[$i] = $NewName;
        }
      }
    } else if($Table == $this->FTableDest) {
      for($i = 0; $i < count($this->FColDest); $i++) {
        if($this->FColDest[$i] == $OldName) {
          $this->FColDest[$i] = $NewName;
        }
      }
    }
  }
  
  function AddColName($Name) {
    $this->FColSource[] = $Name;
  }

  
  function AddDestColName($Name) {
    $this->FColDest[] = $Name;
  }

  function getColSrc() {
    $r = '';
    for($i = 0; $i < count($this->FColSource); $i++)
      $r .= ($i > 0 ? ', ' : '') . $this->FColSource[$i];

    return $r;
  }
  
  function getColDest() {
    $r = '';
    for($i = 0; $i < count($this->FColDest); $i++)
      $r .= ($i > 0 ? ', ' : '') . $this->FColDest[$i];

    return $r;
  }
  
  function getColNamesSource() {
    return $this->FColSource;
  }

  function getColNamesDest() {
    return $this->FColDest;
  }

  function DoLoadXml(DOMElement $xml) {
    $this->OnUpdate = $this->getXMLValue($xml, 'onupdate');
    $this->OnDelete = $this->getXMLValue($xml, 'ondelete');
    $this->MatchType = $this->getXMLValue($xml, 'matchtype');

    $table = $this->getXMLValue($xml, 'source');
    if($table != '') {
      $tbl = $this->getParent()->findObj($table);
      if($tbl !== null && $tbl instanceof Table) {
        $this->setTableSource($tbl);
      }
    }
    
    $table = $this->getXMLValue($xml, 'dest');
    if($table != '') {
      $tbl = $this->getParent()->findObj($table);
      if($tbl !== null && $tbl instanceof Table) {
        $this->setTableDest($tbl);
      }
    }
    
    if($this->getTableSource() !== null && $this->getTableDest() !== null) {
      $tmp = $xml->getElementsByTagName('cols');
      if($tmp->length > 0) {
        foreach($tmp->item(0)->childNodes as $col) {
          $colS = $this->getTableSource()->getColList()->findCol($col->getAttribute('source'));
          $colD = $this->getTableDest()->getColList()->findCol($col->getAttribute('dest'));
        
          if($colS !== null && $colD !== null) {
            $this->AddColName($colS->getName());
            $this->AddDestColName($colD->getName());
          }
        }
      }
    } else {
      print 'Tables for Reference ' . $this->getName() . ' not found ! I will remove this Reference.';
      $this->__destruct();
      //$this = null;
    }
  }

  function DoSaveXml(DOMElement $xml) {
    $xml->appendChild(new DOMElement('source', $this->FTableSource->getName()));
    $xml->appendChild(new DOMElement('dest', $this->FTableDest->getName()));
    $xml->appendChild(new DOMElement('onupdate', $this->OnUpdate));
    $xml->appendChild(new DOMElement('ondelete', $this->OnDelete));
    $xml->appendChild(new DOMElement('matchtype', $this->MatchType));

    if(count($this->FColSource) != count($this->FColDest))
      print 'Reference ' . $this->getName() . ' has different columns counts ! You will lost some data.';

    $cols = $xml->appendChild(new DOMElement('cols'));

    for($i = 0; $i < min(count($this->FColSource), count($this->FColDest)); $i++) {
      $col = $cols->appendChild(new DOMElement('col'));
      $col->setAttribute('source', $this->FColSource[$i]);
      $col->setAttribute('dest', $this->FColDest[$i]);
    }
  }

  function getTableDest() {
    return $this->FTableDest;
  }

  function getTableSource() {
    return $this->FTableSource;
  }

  function setTableDest($Value) {
    if($this->FTableDest !== $Value) {
      if($this->FTableDest !== null)  // remove from old table
        $this->FTableDest->RefDel($this);

      $this->FTableDest = $Value;

      if($this->FTableDest !== null)  // add to new table
        $this->FTableDest->RefAdd($this);
    }
  }

  function setTableSource($Value) {
    if($this->FTableSource !== $Value) {
      if($this->FTableSource !== null)  // remove from old table
        $this->FTableSource->RefDel($this);

      $this->FTableSource = $Value;

      if($this->FTableSource !== null)  // add to new table
        $this->FTableSource->RefAdd($this);
    }
  }
}


define('teBefore', 0);
define('teAfter', 1);
define('tfeRow', 0);
define('tfeStatement', 1);

class Trigger extends AbstractRefObj {
  public $SQL = '';
  public $Execute = teBefore;
  public $_ForEach = tfeRow;
  public $Events = array('i' => false, 'u' => false, 'd' => false);
  private $FTable;

  function DoLoadXml(DOMElement $xml) {
    $table = $this->getXMLValue($xml, 'table');
    if($table != '') {
      $tbl = $this->getParent()->findObj($table);

      if(($tbl !== null) && ($tbl instanceof Table)) {
        $this->setTable($tbl);
      }
    }

    if($this->getTable() == null) {
      print 'Table for Trigger ' + $this->getName() + ' not found ! I will remove this Trigger.';
      $this->__destruct();
    }

    $this->SQL = $this->getXMLValue($xml, 'sql');
    $this->Execute = $this->getXMLValue($xml, 'execute');
    $this->_ForEach = $this->getXMLValue($xml, 'foreach');
    $tmpevents = $this->getXMLValue($xml, 'events');

    if(strpos($tmpevents, 'i') !== false)
      $this->Events['i'] = true;
    if(strpos($tmpevents, 'u') !== false)
      $this->Events['u'] = true;
    if(strpos($tmpevents, 'd') !== false)
      $this->Events['d'] = true;
  }

  function DoSaveXml(DOMElement $xml) {
    $xml->appendChild(new DOMElement('table', $this->FTable->getName()));
    $this->WriteCData($xml, 'sql', $this->SQL);
    $xml->appendChild(new DOMElement('execute', $this->Execute));
    $xml->appendChild(new DOMElement('foreach', $this->_ForEach));
    
    $tmp = '';
    if($this->Events['i'] == true)
      $tmp = 'i';
    if($this->Events['u'] == true)
      $tmp .= 'u';
    if($this->Events['d'] == true)
      $tmp .= 'd';
    
    $xml->appendChild(new DOMElement('events', $tmp));
  }

  function getTable() {
    return $this->FTable;
  }

  function setTable($Value) {
    if($this->FTable !== $Value) {
      if($this->FTable !== null)  // remove from old table
        $this->FTable->RefDel($this);

      $this->FTable = $Value;

      if($this->FTable !== null)  // add to new table
        $this->FTable->RefAdd($this);
    }
  }

  function CheckCol(Table $Table, $ColumnName) {
    return false;
  }
  function Rename(Table $Table, $OldName, $NewName) {}
}

define('spsInvoker', 0);
define('spsDefiner', 1);
define('spvImmutable', 0);
define('spvStable', 1);
define('spvVolatile', 2);

class StoredProcList extends AbstractObjList {}

class StoredProc extends AbstractSQLObj {
  private /*% StoredProcList */ $Procs;

  function __construct(ObjList $Parent, $Name) {
    parent::__construct($Parent, $Name);

    $this->Procs = new StoredProcList();
  }

  function __destruct() {
    parent::__destruct();
  }
  
  function getProcs() {
    return $this->Procs;
  }
  
  function DoLoadXml(DOMElement $xml) {
    $tmp = $xml->getElementsByTagName('procs');
    if($tmp->length > 0) {
      foreach($tmp->item(0)->childNodes as $proc) {
        /*% StoredProcOverload */ $p = new StoredProcOverload();
        $p->LoadFromXml($proc);
        $this->Procs->Add($p);
      }
    }
  }

  function DoSaveXml(DOMElement $xml) {
    $procs = $xml->appendChild(new DOMElement('procs'));
    
    for($i = 0; $i < $this->Procs->getCount(); $i++) {
      $this->Procs->getObj($i)->SaveToXml($procs);
    }
  }
}

class StoredProcOverload extends AbstractDummy {
  public $SQL = '';
  public $Language = '';
  public $Returns = '';
  public $Args = '';
  public $Volatility = spvVolatile;
  public $Security = spsInvoker;
  
  function setName($Value) {
    $this->FName = $Value;
  }

  function LoadFromXml(DOMElement $xml) {
    $this->setName($this->getXMLValue($xml, 'name'));
    $this->setComment($this->getXMLValue($xml, 'comment'));
    
    $this->SQL = $this->getXMLValue($xml, 'sql');
    $this->Language = $this->getXMLValue($xml, 'lang');
    $this->Returns = $this->getXMLValue($xml, 'returns');
    $this->Args = $this->getXMLValue($xml, 'args');
    $this->Volatility = $this->getXMLValue($xml, 'volatility');
    $this->Security = $this->getXMLValue($xml, 'security');

    $this->getProps()->Load($xml);
  }

  function SaveToXml(DOMElement $xml) {
    $proc = $xml->appendChild(new DOMElement('proc'));
    
    $proc->appendChild(new DOMElement('name', $this->getName()));
    $proc->appendChild(new DOMElement('comment', $this->Comment));

    $this->WriteCData($proc, 'sql', $this->SQL);
    $proc->appendChild(new DOMElement('lang', $this->Language));
    $proc->appendChild(new DOMElement('returns', $this->Returns));
    $proc->appendChild(new DOMElement('args', $this->Args));
    $proc->appendChild(new DOMElement('volatility', $this->Volatility));
    $proc->appendChild(new DOMElement('security', $this->Security));

    $this->getProps()->Save($proc);
  }
}

?>