LDAP検索式の構築(式ツリーを使う場合)

前に書いたやつは中置式の構造を作ってLL解析させてたけど、なんか無駄っぽい気がするので最初から式ツリーを構築してその上で検索式を文字列化するようにしてみた。
基本、条件を後から追加する場合前の式の右に位置するように追加して行き、OR直後にANDが来る場合は元の右の式に対して条件を追加する。
ANDばかり、またはORばかりを追加すると木としては左が重くなってしまって式の展開の際スタックを大量に消費する羽目になる。条件が多い場合には前の方がいいかもしれない。
ちなみに式展開を外だしにしているのは同じ検索条件構造をSQLの検索式にも使おうとしているから。もしかしたらXPath検索もありかな。

とか考えたけど結局前のがいいかな、とも思い始めたよ。

class Expression{
	public function compose(Composer $composer){}
}
class ComplexFilter extends Expression{
	var $tree;
	public function  __construct(){		
		$tree = null;
	}
	public function add($filter,$operator = "AND"){
		if($this->tree === null){
			$this->tree = $filter;
			return $this;
		}
		if ($this->tree instanceof ComplexFilterNode) {
			if($this->tree->operator == "OR"){
				if($operator == "AND"){
					$this->tree->right = new ComplexFilterNode($operator, $this->tree->right, $filter);
				}
				else{
					$this->tree = new ComplexFilterNode($operator, $this->tree, $filter);
					
				}
			}
			else{
				$this->tree = new ComplexFilterNode($operator, $this->tree, $filter);
			}
		}
		else{
			$this->tree = new ComplexFilterNode($operator, $this->tree, $filter);
		}
		return $this;
		
	}
	public function addAnd($filter){
		return $this->add($filter,"AND");
	}
	public function addOr($filter){
		return $this->add($filter,"OR");
	}
	function createNode($operator,$left,$right){
		return new ComplexFilterNode($operator,$left,$right);
	}
	public function compose(Composer $composer){
		$composer->composeComplex($this);
	}
}
class ComplexFilterNode extends Filter{ 
	var $left;
	var $right;
	var $operator;
	public function __construct($operator,$left,$right){
		$this->operator = $operator;
		$this->left = $left;
		$this->right = $right;
	}
	public function compose(Composer $composer){
		$composer->composeComplexFilterNode($this);
	}
}
class Filter extends  Expression{
	var $text;
	public function __construct($expression){
		$this->text = $expression;
	}
	public function compose(Composer $composer){
		$composer->composeFilter($this);
	}
}
class Factory{
	public function filter($expression){
		return new Filter($expression);
	}
	public function complex(){
		return new ComplexFilter();
	}
	
}
class Composer{
	
}
class LdapComposer extends Composer{

	public function compose(){
		
	}
	public function composeFilter(Filter $expression){
		$this->text = "(".$expression->text.")";
	}
	public function composeComplex(ComplexFilter $expression){
		$expression->tree->compose($this);
		$this->fixOperator(null, $expression->tree);		
	}
		
	public function composeComplexFilterNode(ComplexFilterNode $expression){
		$leftText =  $this->renderOperand($expression,$expression->left);
		$rightText =  $this->renderOperand($expression,$expression->right);
		
		$this->text =  $leftText . $rightText;
	}
	function renderOperand($parent,$filter){
		$filter->compose($this);
		$this->fixOperator($parent, $filter);
		return $this->text;
	}
	function fixOperator($parent,$filter){
		if($filter instanceof ComplexFilterNode){
			$operator = ($filter->operator == "AND") ? "&" : "|";
			if($parent === null){
				$this->text = "(".$operator.$this->text.")";
			}
			else{
				if($parent->operator != $filter->operator){
					$this->text = "(".$operator.$this->text.")";
				}	
			}
		}
	}
	
	
}

$composer = new LdapComposer();
//0 A
$factory = new Factory();
$filter = $factory->complex()
			->add($factory->filter("A=*"))
			;
$filter->compose($composer);
echo $composer->text. "<br />";

//1 A and B
$factory = new Factory();
$filter = $factory->complex()
			->add($factory->filter("A=*"))
			->addAnd($factory->filter("B=aaa*"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			
//2 A and B
$factory = new Factory();
$filter = $factory->complex()
			->add($factory->filter("A=*"))
			->addOr($factory->filter("B=101*"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			
//3 A and B or C
$factory = new Factory();
$filter = $factory->complex()
			->add($factory->filter("A=*"))
			->addAnd($factory->filter("B=aaa"))
			->addOr($factory->filter("C=ccc"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			

//4 A and B and C
$filter = $factory->complex()
			->add($factory->filter("A"))
			->addAnd($factory->filter("B"))
			->addAnd($factory->filter("C"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			
//5 A or B or C
$filter = $factory->complex()
			->add($factory->filter("A"))
			->addOr($factory->filter("B"))
			->addOr($factory->filter("C"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			
//6 A or B and C
$filter = $factory->complex()
			->add($factory->filter("A"))
			->addOr($factory->filter("B"))
			->addAnd($factory->filter("C"))
			;

$filter->compose($composer);
echo $composer->text. "<br />";
			

//7 A or B and C or D
$filter = $factory->complex()
			->add($factory->filter("A"))
			->addOr($factory->filter("B"))
			->addAnd($factory->filter("C"))
			->addOr($factory->filter("D"))
			;
$filter->compose($composer);
echo $composer->text. "<br />";
						
//8 A or B and C and D
$filter = $factory->complex()
			->add($factory->filter("A"))
			->addOr($factory->filter("B"))
			->addAnd($factory->filter("C"))
			->addAnd($factory->filter("D"))
			;
			
$filter->compose($composer);
echo $composer->text. "<br />";
			

//9 (A or B) and (C or D)
$filter = $factory->complex()
			->add($factory->complex()
				->add($factory->filter("A"))
				->addOr($factory->filter("B"))
			)
			->addAnd($factory->complex()
				->add($factory->filter("C"))
				->addOr($factory->filter("D"))
			)
			;
			
$filter->compose($composer);
echo $composer->text. "<br />";
//10 (A and B G) and (C and D or H)
$filter = $factory->complex()
			->add($factory->complex()
				->add($factory->filter("A"))
				->addAnd($factory->filter("B"))
				->addAnd($factory->filter("G"))
				)
			->addAnd($factory->complex()
				->add($factory->filter("C"))
				->addAnd($factory->filter("D"))
				->addOr($factory->filter("H"))
				)
			;
			
$filter->compose($composer);
echo $composer->text. "<br />";