1対nのアグリゲーションをマップする

ただしA --* B --* C のように1対nのペアだけで構成される場合のみ。
1対1あるいはn対1はいくらあっても良い。
また、双方向関連はまだ。

class RowSet{
    var $data;
    var $position;
    var $rowCount;
    
    public function __construct($data){
        $this->data = $data;
        $this->position = -1;
        $this->rowCount = count($data);
        
    }
    
    public function next(){
        $this->position++;
        if($this->position>= $this->rowCount){
            return false;
        }
        return true;
    }
    public function current(){
        return $this->data[$this->position];
    }
}

class EntityMapper{
    var $previousRow;
    var $currentRow;
    var $rowSet;
    var $position;
    var $hasMoreRow;
    var $metadata;
    public function __construct($meta,$rowSet){
        $this->rowSet = $rowSet;
        $this->previousRow = array();
        $this->currentRow = array();
        $this->hasMoreRow = false;
        $this->metadata = $meta;
        $this->position = 0;
    }
    public function map(){
        $this->moveNext();
        $entities = array();
        $path = $this->newPath(array(),$this->metadata);
        $keys = array();
        while($this->hasMoreRow){
            $this->currentRow = $this->rowSet->current();
            $entity = $this->mapEntity($this->metadata);
            $entities[] = $entity;
            $this->previousRow = $this->currentRow;
            $seek = $this->mapAssociation($entity,$path,$keys,$this->metadata);
            if($seek == 0){
                $this->moveNext();
            }
            if($this->position> 1000) break;;
        }
        return $entities;
    }
    function mapAssociation($entity,$path,$keys,$meta){
            $this->previousRow = $this->currentRow;
        $this->position++;
        if($this->position> 1000) return;
        $newKeys = $this->newKeys($keys,$meta);
        $seek = 0;
        foreach($meta->hasOne as $name => $assoc){
            $seek += $this->mapToOne($entity,$path,$newKeys,$assoc);
        }                               
        foreach($meta->belongsTo as $name => $assoc){
            $seek += $this->mapToOne($entity,$path,$newKeys,$assoc);
        }
        
        foreach($meta->hasMany as $name => $assoc){
            $this->mapToMany($entity,$path,$newKeys,$assoc);
            $seek++;
        }
        return $seek;
    }
    function mapToOne($entity,$path,$keys,$meta){
        $newEntity = $this->mapEntity($meta);
        $fieldName = $meta->name;
        $entity->$fieldName = $newEntity;
        
        $newPath = $this->newPath($path,$meta);
        $seek = $this->mapAssociation($newEntity,$newPath,$keys,$meta);
        return $seek;
    }
    function mapToMany($entity,$path,$keys,$meta){
        $fieldName = $meta->name;
        $entity->$fieldName = array();
        $newPath = $this->newPath($path,$meta);
        
        while($this->hasMoreRow){
                echo "keys :" . implode($keys,",") . "<br />";
            $this->position++;
            if($this->position> 1000) return;
            
            if($this->breaks($keys)){
                break;
            }
            $newEntity = $this->mapEntity($meta);
            $entity->$fieldName  = array_merge($entity->$fieldName,array($newEntity));
            $seek = $this->mapAssociation($newEntity,$newPath,$keys,$meta);
            if($seek == 0){
                $this->moveNext();
                
            }

        }
        return 1;
    }
    function mapEntity($meta){
        $entity = new Entity();
        foreach($meta->fields as $field){
            $columnName = $meta->name . "__" . $field;
            if(array_key_exists($columnName,$this->currentRow)){
                $entity->$field = $this->currentRow[$columnName];
            }
        }
        return $entity;
    }
    function breaks($keys){
        $equals = true;
        foreach($keys as $key){
            if(!array_key_exists($key,$this->previousRow)){
                $equals = false;
                break;
            } 
            if($this->previousRow[$key] == null){
                $equals = false;
                break;
            } 
            if($this->currentRow[$key] != $this->previousRow[$key]){
                echo $this->currentRow[$key]. " : " .$this->previousRow[$key] . "  /  ";
                $equals = false;
                break;
                
            }
        }    
        return !$equals;
    }
    
    function newPath($path,$meta){
        $newPath = array_merge($path,array($meta->name));
        return $newPath;
    }
    function newKeys($keys,$meta){
        $newKeys = array();
        foreach($meta->primaryKeys as $key){
            $newKeys[] = $meta->name. "__" . $key;
        }
        $newKeys = array_merge($keys,$newKeys);
        return $newKeys;
    }
    
    function moveNext(){
        $this->hasMoreRow = $this->rowSet->next();
        $this->currentRow = $this->rowSet->current();
    }
}
class Entity{
    var $rowData;
    public function __construct(){
     $this->rowData = array();   
    }
    public function __get($name){
        return $this->rowData[$name];
    }
    public function __set($name,$value){
        $this->rowData[$name] = $value;
        
    }
    
}
class Meta{
    var $name;
    var $fields;
    var $primaryKeys;
    var $hasMany;
    var $hasOne;
    var $belongsTo;
    
    public function __construct(){
        $this->name = "";
        $this->fields = array();
        $this->primaryKeys= array();
        $this->hasMany = array();
        $this->hasOne = array();
        $this->belongsTo = array();
    }
}