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

	public function  weaveBeforeAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$rootElement = $joinPoint->getAST();
		
		// 織り込みを行うPHP文の、配列を作成。
		$weavingElementArray = array();
		// includeの為のautoload関数の設定。
		$weavingElementArray[] = AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath());
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$weavingElementArray[] = new AOWP_PHPStatementElement($aspectInstantiationElement);
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$weavingElementArray[] = new AOWP_PHPStatementElement($joinPointInstantiationElement);
		// アドバイスを実行するIf文。
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionAST($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName());
		$weavingElementArray[] = $adviceExecutionElement;

		// 対象のスクリプトファイルに、織り込みを行うASTを追加。
		$rootElement->insertElement(new AOWP_PHPTagElement(null, $weavingElementArray), 0);
	}

	public function  weaveAfterAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$rootElement = $joinPoint->getAST();
		$wovenElementArray = array();
		
		// 織り込みを行うPHP文を、追加。
		// includeの為のautoload関数の設定。
		$wovenElementArray[] = AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath());
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$wovenElementArray[] = new AOWP_PHPStatementElement($aspectInstantiationElement);
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$wovenElementArray[] = new AOWP_PHPStatementElement($joinPointInstantiationElement);
		// アドバイスを実行するIf文。
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionAST($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName());
		$wovenElementArray[] = $adviceExecutionElement;
		
		// 対象のスクリプトファイルに、織り込みを行うASTを追加。
		$rootElement->setElement(new AOWP_PHPTagElement(null, $wovenElementArray));
	}

	public function  weaveAroundAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$rootElement = $joinPoint->getAST();
		
		// Backup original program's elements and refresh the targeted script file's statements.
		$originalElements = $rootElement->statements;
		$rootElement->statements = array();
		
		// 織り込みの実装。
		$wovenElementArray = array();

		// includeの為のautoload関数の設定。
		$wovenElementArray[] = AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath());

		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$wovenElementArray[] = new AOWP_PHPStatementElement($aspectInstantiationElement);

		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$wovenElementArray[] = new AOWP_PHPStatementElement($joinPointInstantiationElement);

		// アドバイスを実行するIf文。
		$adviceExecutionIfStatement = new AOWP_PHPIfStatementElement();
		$runtimeMatchMethodCall = new AOWP_PHPSimpleMethodCallElement($aspectInstantiationElement->getLeftVarialeName(), 'runtimeMatch');
		$runtimeMatchMethodCall->addArgument(AOWP_PHPArgumentElement::createStringArgument($advice->getIndex()));
		$runtimeMatchMethodCall->addArgument(AOWP_PHPArgumentElement::createVariableArgument($joinPointInstantiationElement->getLeftVarialeName()));
		$adviceExecutionIfStatement->setCondition($runtimeMatchMethodCall);
		
		// Define labels and a variable name for the weaving.
		$proceedLabel = AOWP_WeavingASTHelper::getRandomName("proceed", false);
		$proceedReturnBaseLabel = AOWP_WeavingASTHelper::getRandomName("proceedReturn", false) . '_';
		$proceedEndLabel = AOWP_WeavingASTHelper::getRandomName("proceedEnd", false);
		$proceedReturnFlagVariableName = AOWP_WeavingASTHelper::getRandomName('proceedReturnFlag', true);
		
		// Convert advice body.
		$adviceBodyConverter = new AOWP_AdviceBodyConverterForScriptExecutionWeaving(
			$advice, $proceedLabel, $proceedReturnBaseLabel, 
			$proceedReturnFlagVariableName, $aspectInstantiationElement->getLeftVarialeName(),
			$joinPointInstantiationElement->getLeftVarialeName(), $proceedEndLabel);
		// Set advice body into the above if element's inner statements.
		$adviceExecutionIfStatement->innerStatements = array(new AOWP_PHPInnerStatementElement($adviceBodyConverter->getConvertedAdviceBodyElements()));
		
		// Add elements for releasing an aspect instance when advice is not executed.
		$adviceExecutionIfStatement->addElseStatement(new AOWP_PHPStatementElement(AOWP_WeavingASTHelper::createAspectInstanceReleaseAST($aspectInstantiationElement->getLeftVarialeName())));
		
		// Add if element for executing advice or original code into woven element.
		$wovenElementArray[] = $adviceExecutionIfStatement;
		
		// Add elements of original program execution into woven source code.
		$convertedOriginalElements = AOWP_WeavingASTHelper::convertOriginalElementsForScriptExecutionAroundAdvice($originalElements, $proceedLabel, $proceedReturnFlagVariableName, $adviceBodyConverter->getProceedReturnLabels(), $proceedEndLabel);
		$wovenElementArray = array_merge($wovenElementArray, $convertedOriginalElements);
		
		// Release the aspect instance.
		$wovenElementArray[] = new AOWP_PHPStatementElement(AOWP_WeavingASTHelper::createAspectInstanceReleaseAST($aspectInstantiationElement->getLeftVarialeName()));
		
		// 元のコードに追加。
		$rootElement->setElement(new AOWP_PHPTagElement(null, $wovenElementArray));
	}
}
?>