<?php
/**
 * 
 * @author keiji
 * @package aowp.aspect.weaver.command
 */
/**
 * 
 * @author keiji
 * @package aowp.aspect.weaver.command
 */
class AOWP_ExitJoinPointWeaveCommand implements AOWP_IWeaveCommand {

	/**
	 * @see aspect/weaver/command/AOWP_IWeaveCommand#weaveBeforeAdvice()
	 * @param mixed $advice {@link AOWP_BeforeAdvice}
	 * @param mixed $joinPoint {@link AOWP_ExitJoinPoint}
	 */
	public function  weaveBeforeAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$exitElement = $joinPoint->getAST();

		// ラッピングする関数を作成。
		$wrappingFunctionElement = new AOWP_PHPFunctionElement(AOWP_WeavingASTHelper::getRandomName('function'));
		if ($exitElement->expr != null) {
			$wrappingFunctionElement->addParameter(AOWP_WeavingASTHelper::getRandomName('parameter', true));
		}
		// includeの為のautoload関数の設定。
		$wrappingFunctionElement->setElement(AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath()));
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($aspectInstantiationElement));
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($joinPointInstantiationElement));
		// 関数呼び出しの引き数を、ジョインポイントに設定。
		for ($i = 0; $i < $wrappingFunctionElement->getParameterCount(); $i++) {
			$joinPointArgumentSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'addArgument');
			$joinPointArgumentSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($wrappingFunctionElement->getParameterName($i)));
			$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($joinPointArgumentSetElement));
		}
		// アドバイスを実行するIf文。
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionAST($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName());
		$wrappingFunctionElement->setElement($adviceExecutionElement);
		// リターン文。
		$returnExitElement = new AOWP_PHPExitExprElement(null, $exitElement->expr);
		$returnStatementElement = new AOWP_PHPReturnStatementElement(null, $returnExitElement);
		$wrappingFunctionElement->setElement($returnStatementElement);
		
		// ラッピング関数の重複定義を防ぐためのIf文を追加。
		$ifFunctionDeclarationElement = AOWP_WeavingASTHelper::createIfForFunctionDeclaration($wrappingFunctionElement);
		
		// ラッピング関数を、元のコードに追加。
		AOWP_WeavingASTHelper::insertElement($exitElement, $ifFunctionDeclarationElement);
				
		// 対象のメソッド呼び出しを、作成したラッピング関数の呼び出しに変更する。
		AOWP_ExitJoinPointWeaveCommand::_changeIntoWrapingFunctionCall($exitElement, $wrappingFunctionElement->getFunctionName());
	}

	/**
	 * Do not weave after call of exit().
	 * 
	 * @see aspect/weaver/command/AOWP_IWeaveCommand#weaveAfterAdvice()
	 */
	public function  weaveAfterAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		
	}

	public function  weaveAroundAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$exitElement = $joinPoint->getAST();
		
		// ラッピングする関数を作成。
		$wrappingFunctionElement = new AOWP_PHPFunctionElement(AOWP_WeavingASTHelper::getRandomName('function'));
		if ($exitElement->expr != null) {
			$wrappingFunctionElement->addParameter(AOWP_WeavingASTHelper::getRandomName('parameter', true));
		}
		// includeの為のautoload関数の設定。
		$wrappingFunctionElement->setElement(AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath()));
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($aspectInstantiationElement));
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($joinPointInstantiationElement));
		// 関数呼び出しの引き数を、ジョインポイントに設定。
		for ($i = 0; $i < $wrappingFunctionElement->getParameterCount(); $i++) {
			$joinPointArgumentSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'addArgument');
			$joinPointArgumentSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($wrappingFunctionElement->getParameterName($i)));
			$wrappingFunctionElement->setElement(new AOWP_PHPStatementElement($joinPointArgumentSetElement));
		}
		// アドバイスを実行するIf文。
		$originalExitElement = new AOWP_PHPExitExprElement(null, $exitElement->expr);
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionASTForAround($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName(), $originalExitElement);
		$wrappingFunctionElement->setElement($adviceExecutionElement);
		
		// ラッピング関数の重複定義を防ぐためのIf文を追加。
		$ifFunctionDeclarationElement = AOWP_WeavingASTHelper::createIfForFunctionDeclaration($wrappingFunctionElement);

		// ラッピング関数を、元のコードに追加。
		AOWP_WeavingASTHelper::insertElement($exitElement, $ifFunctionDeclarationElement);
				
		// 対象のメソッド呼び出しを、作成したラッピング関数の呼び出しに変更する。
		AOWP_ExitJoinPointWeaveCommand::_changeIntoWrapingFunctionCall($exitElement, $wrappingFunctionElement->getFunctionName());
	}

	private static function _changeIntoWrapingFunctionCall(AOWP_PHPExitExprElement &$targetExitElement, $wrappingFunctionName) {
		// 対象のメソッド呼び出しを、作成したラッピング関数の呼び出しに変更する。
		$parentElement = $targetExitElement->getParent();
		// ラッピング関数の呼び出しAST。
		$wrappingFunctionCallElement = new AOWP_PHPFunctionCallElement($wrappingFunctionName);
		if ($targetExitElement->expr != null) {
			$wrappingFunctionCallElement->addArgument(new AOWP_PHPArgumentElement(null, $targetExitElement->expr));
		}
		// 元のメソッド呼び出しと、置き換え。
		$parentElement->{$targetExitElement->getParentPropertyName()} = $wrappingFunctionCallElement;
	}
	
}

?>