<?php
////////////////////////////////////////////////////////////////////////////////
// [TreeDBMClass] Tree DBM Class
// modifyed by FUKAYA Takashi 2001.01.08
//
// Original code programmbed by KUROKI Junichi
//     class       : SDBMClass
//     description : sequence access dbm wrapper
////////////////////////////////////////////////////////////////////////////////

class TreeDBMClass {

/* doc

 主にツリー形式掲示板 発言保存用クラス

● レコード

    #            記事#
    last#        ツリー階層ごとの記事数
    key#        #番目の投稿のkey名
    lastkey        key数

● データ例

KEY(データ本体)    KEY(各階層の最大値) VALUE
------------------ ------------------- -----
                   last               = 3    
1                  last1              = 2    
1_1                last1_1            = 1    
1_1_1              last1_1_1          = null
1_2                last1_2            = 1
1_2_1              last1_2_1          = 1
1_2_1_1            last1_2_1_1        = 1
1_2_1_1_1          last1_2_1_1_1      = null
2                  last2              = null
3                  last3              = 1
3_1                last3_1            = 2
3_1_1              last3_1_1          = null
3_1_2              last1_1_2          = null


 last〜 キーのレコードは、各階層からいくつノードが出ているかを保持している
 ・その階層が リーフの場合は、NULLというか last〜 レコードが存在しない
 ・DB構築直後は、ルートからのノードは0件なので、「leaf」レコードも存在しない
 ・階層の制限は、DBMのキーに許される長さに依存する

● メソッド

・insertLeaf( $nodekey , $data );
 ノード $nodekey 以下にリーフ $data を追加
  (発言に対し返信する時に使用)

 ex.) ルート発言
       引数:    insertLeaf( "" , $data );
       内部処理:レコード「last」の値を取得、その値+1のキーで $dataを保存
                 (lastが存在しない場合、キーは「1」)
                 (lastの値が、「1」の場合、キーは「2」)
                 lastの値を+1する

 ex.) 発言(key=1)への返信
       引数:    insertLeaf( "1" , $data );
       内部処理:レコード「last1」の値を取得、$nodekey + その値+1のキーで $dataを保存
                 (last1が存在しない場合、キーは「1_1」)
                 (last1の値が、「1」の場合、キーは「1_2」)
                 last1の値を+1する

 レコードの削除は考えていない。削除フラグを用意するとかする
*/


  // Variable --------------------
//var $wsequence ;
//var $rsequence ;
  
var $name ;            // データベース名
  
var $id ;                // DBMのID
  
var $result ;            // ?
//var $eof ;            // 最終レコードか?
  
var $record ;            // レコードフィールド定義(コレクション)
  
var $colnum ;            // フィールド数


  // Method --------------------

  ////////////////////////////////////////////////////////////////////////////////
  // method      : dump
  // description : dump variable
  // -----------------------------------------------------------------------------
  // input  : none
  // return : none
  ////////////////////////////////////////////////////////////////////////////////
  
function dump() {
    echo 
"-- DUMP --------<br>\n" ;
//    echo "\$wsequence = $this->wsequence<br>\n" ;
//    echo "\$rsequence = $this->rsequence<br>\n" ;
    
echo "\$name      = $this->name<br>\n" ;
    echo 
"\$id        = $this->id<br>\n" ;
    echo 
"\$result    = $this->result<br>\n" ;
//  echo "\$eof       = $this->eof<br>\n" ;
    
echo "\$record    = $this->record<br>\n" ;
    echo 
"\$colnum    = $this->colnum<br>\n" ;
    echo 
"-------- DUMP --<br>\n" ;
  }

  function 
dumpRecords() {
    echo 
"-- DUMP All Records --------<br>\n" ;

    
$dbmkey dbmfirstkey ($this->id);
    while (
$dbmkey) {
        echo 
"$dbmkey = " dbmfetch($this->id,$dbmkey) . "<BR>\n";
        
$dbmkey dbmnextkey ($this->id$dbmkey);
    }

    echo 
"-------- DUMP All Records ---<br>\n" ;
  }


  
////////////////////////////////////////////////////////////////////////////////
  // method      : TreeDBMClass
  // description : check & initialize database
  // -----------------------------------------------------------------------------
  // input  : dbname : database name
  // return : none
  ////////////////////////////////////////////////////////////////////////////////
  
function TreeDBMClass$dbname ) {

    
$this->name $dbname ;

    
// Database check & initialize --------------------
    
if( file_exists$this->name ".db" ) || file_exists$this->name ) ) {

      
// Open database
      
$this->id dbmopen$this->name"w" ) ;
      
$record       dbmfetch$this->id"record" ) ;
      
$this->record explode","$record ) ;
      
$this->colnum count$this->record ) ;

    } else {

      
// Create database
      
$this->id dbmopen$this->name"n" ) ;
//      dbminsert( $this->id, "sequence", 0 ) ;
      
$this->record    = array() ;
      
$this->colnum    ;

    }
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : clear
  // description : clear database
  // -----------------------------------------------------------------------------
  // input  : none
  // return : none
  ////////////////////////////////////////////////////////////////////////////////
  
function clear() {
    
// 消すことは考えてない
  
}


  
////////////////////////////////////////////////////////////////////////////////
  // method      : defrecord
  // description : efine record
  // -----------------------------------------------------------------------------
  // input  : record : define column names
  // return : none
  ////////////////////////////////////////////////////////////////////////////////
  
function defrecord$record ) {

    
$this->record explode","$record ) ;
    
$this->colnum count$this->record ) ;
    
dbmreplace$this->id"record"$record ) ;

  }


  
////////////////////////////////////////////////////////////////////////////////

  // 指定キー階層の要素数を返す
  
function getNodeNum($nodekey "" ) {
    
$last $this->fetch"last" $nodekey );
    if ( 
$last == "" $last 0;
    return 
$last;
  }


  
// 指定キーの階層を返す(ルート=1)
  
function getNodeLevel($nodekey) {
     
$levels explode("_"$nodekey);
     return 
count($levels);
  }


  
// $base以降のツリーのキーリストを$keys配列にセットする
  
function makeList( &$keys $base="" $n=) {
    if ( !
$this->exists$base $n ) ) return;
//    echo "add : " .$base . $n . "  cnt=" . count($keys) . "<BR>\n";
    
$keys[  ] = $base $n;
    
$this->makeList$keys $base $n "_" );        // 子ノード探索
    
$this->makeList$keys $base $n+);                // 次ノード探索 (兄弟探しも再帰なのは手抜きすぎか))
    
return;
  }

    
// $base以降のツリーのキーリストを$keys配列にセットする (ルート階層は逆順)
    
function makeListRev( &$keys $base=""  ) {
        for (
$n=$this->fetch('last'); $n>0$n-- ) {
            
$keys[  ] = $n;
            
$this->makeList$keys $n '_' );
        }
    }

    
// レコードフィールド定義に従って、配列をTAB区切り文字列に変換
    
function margeData( &$data ) {
        
$result="";
        for( 
$i=0$i<$this->colnum$i++ ) {
          if( 
$i>$result .= "\t" ;
          
$result .= str_replace"\t"," "$data[$this->record[$i]] ) ;        // TABは空白に変換
        
}
        return 
$result;
    }
    
    
// レコードフィールド定義に従って、TAB区切り文字列から配列に変換
    
function splitData$data , &$result) {
        
$src explode"\t"$data ) ;
        
$result = array();
        for( 
$i=0$i<$this->colnum$i++ ) {
             
$result[$this->record[$i]] = $src[$i];    
        }
    }

    
// ツリーにレコードを追加 (+TAB区切り文字への変換)
    
function insertLeafR$nodekey , &$data ) {
        return 
$this->insertLeaf$nodekey $this->margeData$data ) );
    }

    
// ツリーにレコードを更新 (+TAB区切り文字への変換)
    
function replaceR$key, &$data ) {
        return 
$this->replace$key $this->margeData$data ) );
    }

    
// ツリーにレコードを取得 (+配列の変換)
    
function fetchR$key , &$result ) {
        return 
$this->splitdata$this->fetch$key ) , $result );
    }


  
////////////////////////////////////////////////////////////////////////////////
  // method      : insertLeaf (F/T)
  // description : insert leaf data
  // -----------------------------------------------------------------------------
  // input  : node : node name (parent key)
  //        : data : data
  // return : result code
  ////////////////////////////////////////////////////////////////////////////////
  
function insertLeaf$nodekey$data ) {
    
$lastnodekey "last" $nodekey;
    
$last $this->fetch$lastnodekey );
    if ( 
$last == "" $last=1; else $last+=1;
    
$this->replace$lastnodekey $last );
    
    if ( 
$nodekey != "" $leafkey $nodekey "_" $last; else $leafkey $last
    
    
$stat $this->insert(  $leafkey$data ) ;

    
// KEY# 記録
    
$nextkey $this->fetch("lastkey") + 1;
    
$this->insert"key" $nextkey $leafkey );
    
$this->replace("lastkey",$nextkey);
    
    return 
$stat;
  }
  
  
  

  
////////////////////////////////////////////////////////////////////////////////
  // method      : exist (RAW)
  // description : check exist
  // -----------------------------------------------------------------------------
  // input  : key
  // return : exist
  ////////////////////////////////////////////////////////////////////////////////
  
function exists$key ) {
    return 
dbmexists$this->id$key ) ;
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : fetch (RAW)
  // description : fetch data
  // -----------------------------------------------------------------------------
  // input  : key
  // return : data
  ////////////////////////////////////////////////////////////////////////////////
  
function fetch$key ) {
    return 
dbmfetch$this->id$key ) ;
  }


  
////////////////////////////////////////////////////////////////////////////////
  // method      : firstkey (RAW)
  // description : read first key
  // -----------------------------------------------------------------------------
  // input  : none
  // return : key
  ////////////////////////////////////////////////////////////////////////////////
  
function firstkey() {
    return 
dbmfirstkey$this->id ) ;
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : nextkey (RAW)
  // description : read next key
  // -----------------------------------------------------------------------------
  // input  : current key
  // return : key
  ////////////////////////////////////////////////////////////////////////////////
  
function nextkey$key ) {
    return 
dbmnextkey$this->id$key ) ;
  }



  
////////////////////////////////////////////////////////////////////////////////
  // method      : insert (RAW)
  // description : insert data
  // -----------------------------------------------------------------------------
  // input  : key  : key
  //        : data : data
  // return : result code
  ////////////////////////////////////////////////////////////////////////////////
  
function insert$key$data ) {
      return 
dbminsert$this->id$key$data ) ;
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : replace (RAW)
  // description : replace data
  // -----------------------------------------------------------------------------
  // input  : key  : key
  //        : data : data
  // return : result code
  ////////////////////////////////////////////////////////////////////////////////
  
function replace$key$data ) {
    return 
dbmreplace$this->id$key$data ) ;
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : delete (RAW)
  // description : delete data
  // -----------------------------------------------------------------------------
  // input  : key  : key
  // return : result code
  ////////////////////////////////////////////////////////////////////////////////
  
function delete$key ) {
    return 
dbmdelete$this->id$key ) ;
  }

  
////////////////////////////////////////////////////////////////////////////////
  // method      : close (RAW)
  // description : close dbm
  // -----------------------------------------------------------------------------
  // input  : none
  // return : none
  ////////////////////////////////////////////////////////////////////////////////
  
function close() {
    
dbmclose$this->id ) ;
  }

}

// end of script ///////////////////////////////////////////////////////////////
?>