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

	public function  weaveBeforeAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$methodElement = $joinPoint->getAST();
		$classElement = $methodElement->getParent();
		
		// 元のメソッドと同名のメソッドを作成。
		$newMethodElement = new AOWP_PHPMethodElement();
		$newMethodElement->setMethodName($methodElement->getMethodName());
		$newMethodElement->setModifiers($methodElement->getModifiers());
		$newMethodElement->isReference = $methodElement->isReference;
		foreach ($methodElement->paramaters as $parameter) {
			$newMethodElement->paramaters[] = (clone $parameter);
		}
		
		// 元のメソッドの名前を変えて、他のprivateメソッドの作成。
		$methodElement->setMethodName(AOWP_WeavingASTHelper::getRandomName('_method'));
		$methodElement->removeModifier(AOWP_PHPMethodElement::PUBLIC_MODIFER);
		$methodElement->addModifier(AOWP_PHPMethodElement::PRIVATE_MODIFER);
		
		// 元のメソッドと同名のメソッドの実装。
		// includeの為のautoload関数の設定。
		$newMethodElement->setElement(AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath()));
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$newMethodElement->setElement(new AOWP_PHPStatementElement($aspectInstantiationElement));
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInstantiationElement));
		// 呼び出されるオブジェクトを、ジョインポイントに設定。
		$joinPointInvokedObjectSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'setInvokedObject');
		$joinPointInvokedObjectSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument('$this'));
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInvokedObjectSetElement));
		// メソッド呼び出しの引き数を、ジョインポイントに設定。
		foreach ($newMethodElement->getParameters() as $parameter) {
			$joinPointArgumentSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'addArgument');
			$joinPointArgumentSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($parameter->getParameterName()));
			$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointArgumentSetElement));
		}
		// アドバイスを実行するIf文。
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionAST($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName());
		$newMethodElement->setElement($adviceExecutionElement);
		// return文。
		$originalMethodCallElement = new AOWP_PHPSimpleMethodCallElement('$this', $methodElement->getMethodName());
		for ($i = 0; $i < $newMethodElement->getParameterCount(); $i++) {
			$joinPointGetParameterElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'getArgument');
			$joinPointGetParameterElement->addArgument(AOWP_PHPArgumentElement::createStringArgument($i));
			$originalMethodCallElement->addArgument(new AOWP_PHPArgumentElement(null, $joinPointGetParameterElement));
		}
		$returnElement = new AOWP_PHPReturnStatementElement(null, $originalMethodCallElement);
		$newMethodElement->setElement($returnElement);
		
		// 新たに作成した、元のメソッドと同名のメソッドを、クラス定義に追加。
		$classElement->addStatement($newMethodElement);
	}

	public function  weaveAfterAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$methodElement = $joinPoint->getAST();
		$classElement = $methodElement->getParent();
		
		// 元のメソッドと同名のメソッドを作成。
		$newMethodElement = new AOWP_PHPMethodElement();
		$newMethodElement->setMethodName($methodElement->getMethodName());
		$newMethodElement->setModifiers($methodElement->getModifiers());
		$newMethodElement->isReference = $methodElement->isReference;
		foreach ($methodElement->paramaters as $parameter) {
			$newMethodElement->paramaters[] = (clone $parameter);
		}
		
		// 元のメソッドの名前を変えて、他のprivateメソッドの作成。
		$methodElement->setMethodName(AOWP_WeavingASTHelper::getRandomName('_method'));
		$methodElement->removeModifier(AOWP_PHPMethodElement::PUBLIC_MODIFER);
		$methodElement->addModifier(AOWP_PHPMethodElement::PRIVATE_MODIFER);
		
		// 元のメソッドと同名のメソッドの実装。
		// includeの為のautoload関数の設定。
		$newMethodElement->setElement(AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath()));
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$newMethodElement->setElement(new AOWP_PHPStatementElement($aspectInstantiationElement));
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInstantiationElement));
		// 元のメソッドの実行し、その結果をジョインポイントに設定。
		$joinPointReturnSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'setReturnValue');
		$originalMethodCallElement = new AOWP_PHPSimpleMethodCallElement('$this', $methodElement->getMethodName());
		for ($i = 0; $i < $newMethodElement->getParameterCount(); $i++) {
			$originalMethodCallElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($newMethodElement->getParameterName($i)));
		}
		$joinPointReturnSetElement->addArgument(new AOWP_PHPArgumentElement(null, $originalMethodCallElement));
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointReturnSetElement));
		// 呼び出されるオブジェクトを、ジョインポイントに設定。
		$joinPointInvokedObjectSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'setInvokedObject');
		$joinPointInvokedObjectSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument('$this'));
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInvokedObjectSetElement));
		// メソッド呼び出しの引き数を、ジョインポイントに設定。
		foreach ($newMethodElement->getParameters() as $parameter) {
			$joinPointArgumentSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'addArgument');
			$joinPointArgumentSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($parameter->getParameterName()));
			$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointArgumentSetElement));
		}
		// アドバイスを実行するIf文。
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionAST($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName());
		$newMethodElement->setElement($adviceExecutionElement);
		// return文。
		$joinPointReturnGetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'getReturnValue');
		$returnElement = new AOWP_PHPReturnStatementElement(null, $joinPointReturnGetElement);
		$newMethodElement->setElement($returnElement);
		
		// 新たに作成した、元のメソッドと同名のメソッドを、クラス定義に追加。
		$classElement->addStatement($newMethodElement);
	}

	public function  weaveAroundAdvice(AOWP_Advice &$advice, AOWP_JoinPoint &$joinPoint) {
		$methodElement = $joinPoint->getAST();
		$classElement = $methodElement->getParent();
		
		// 元のメソッドと同名のメソッドを作成。
		$newMethodElement = new AOWP_PHPMethodElement();
		$newMethodElement->setMethodName($methodElement->getMethodName());
		$newMethodElement->setModifiers($methodElement->getModifiers());
		$newMethodElement->isReference = $methodElement->isReference;
		foreach ($methodElement->paramaters as $parameter) {
			$newMethodElement->paramaters[] = (clone $parameter);
		}
		
		// 元のメソッドの名前を変えて、他のpublicメソッドの作成 (proceedで呼ばれる事がある為、public)。
		$methodElement->setMethodName(AOWP_WeavingASTHelper::getRandomName('_method'));
		$methodElement->removeModifier(AOWP_PHPMethodElement::PRIVATE_MODIFER);
		$methodElement->addModifier(AOWP_PHPMethodElement::PUBLIC_MODIFER);
		
		// 元のメソッドと同名のメソッドの実装。
		// includeの為のautoload関数の設定。
		$newMethodElement->setElement(AOWP_WeavingASTHelper::createIncludeStatemenetElement($joinPoint->getFileFullPath()));
		// アスペクトのインスタンス化。
		$aspectInstantiationElement = AOWP_WeavingASTHelper::createAspectInstantiationAST($advice->getClassNameOfAspect());
		$newMethodElement->setElement(new AOWP_PHPStatementElement($aspectInstantiationElement));
		// ジョインポイントのインスタンス化。
		$joinPointInstantiationElement = AOWP_WeavingASTHelper::createJoinPointInstantiationAST($joinPoint);
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInstantiationElement));
		// 呼び出されるオブジェクトを、ジョインポイントに設定。
		$joinPointInvokedObjectSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'setInvokedObject');
		$joinPointInvokedObjectSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument('$this'));
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointInvokedObjectSetElement));
		// proceed用のメソッド名を設定。
		$joinPointProceedSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'setProceedFunctionName');
		$joinPointProceedSetElement->addArgument(AOWP_PHPArgumentElement::createStringArgument($methodElement->getMethodName()));
		$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointProceedSetElement));
		// メソッド呼び出しの引き数を、ジョインポイントに設定。
		foreach ($newMethodElement->getParameters() as $parameter) {
			$joinPointArgumentSetElement = new AOWP_PHPSimpleMethodCallElement($joinPointInstantiationElement->getLeftVarialeName(), 'addArgument');
			$joinPointArgumentSetElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($parameter->getParameterName()));
			$newMethodElement->setElement(new AOWP_PHPStatementElement($joinPointArgumentSetElement));
		}
		// アドバイスを実行するIf文。
		$originalExecutionElement = new AOWP_PHPSimpleMethodCallElement('$this', $methodElement->getMethodName());
		for ($i = 0; $i < $newMethodElement->getParameterCount(); $i++) {
			$originalExecutionElement->addArgument(AOWP_PHPArgumentElement::createVariableArgument($newMethodElement->getParameterName($i)));
		}
		$adviceExecutionElement = AOWP_WeavingASTHelper::createAdviceExecutionASTForAround($aspectInstantiationElement->getLeftVarialeName(),
			$advice->getIndex(), $joinPointInstantiationElement->getLeftVarialeName(), $originalExecutionElement);
		$newMethodElement->setElement($adviceExecutionElement);
		
		// 新たに作成した、元のメソッドと同名のメソッドを、クラス定義に追加。
		$classElement->addStatement($newMethodElement);
	}
}

?>