/**
  * Will generate the skeleton code for the passed function definition.
  * Will result in a string resembling the following example:
  *
  *      <FUNCTION_DOCBLOCK>
  *      <FUNCTION_MODIFIERS> function <FUNCTION_NAME>(<FUNCTION_PARAMS>)
  *      {
  *          $dgStartLine = <FUNCTION_START_LINE>;
  *          $dgEndLine = <FUNCTION_END_LINE>;
  *          / DOPPELGAENGER_FUNCTION_BEGIN_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_BEFORE_JOINPOINT <FUNCTION_NAME> /
  *          $dgOngoingContract = \AppserverIo\Doppelgaenger\ContractContext::open();
  *          / DOPPELGAENGER_INVARIANT_PLACEHOLDER /
  *          / DOPPELGAENGER_PRECONDITION_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_OLD_SETUP_PLACEHOLDER <FUNCTION_NAME> /
  *          $dgResult = null;
  *          try {
  *              / DOPPELGAENGER_AROUND_JOINPOINT <FUNCTION_NAME> /
  *
  *          } catch (\Exception $dgThrownExceptionObject) {
  *              / DOPPELGAENGER_AFTERTHROWING_JOINPOINT <FUNCTION_NAME> /
  *
  *              // rethrow the exception
  *              throw $dgThrownExceptionObject;
  *
  *          } finally {
  *              / DOPPELGAENGER_AFTER_JOINPOINT <FUNCTION_NAME> /
  *
  *          }
  *          / DOPPELGAENGER_POSTCONDITION_PLACEHOLDER <FUNCTION_NAME> /
  *          / DOPPELGAENGER_INVARIANT_PLACEHOLDER /
  *          if ($dgOngoingContract) {
  *              \AppserverIo\Doppelgaenger\ContractContext::close();
  *          } / DOPPELGAENGER_AFTERRETURNING_JOINPOINT <FUNCTION_NAME> /
  *
  *          return $dgResult;
  *      }
  *
  * @param boolean            $injectNeeded       Determine if we have to use a try...catch block
  * @param FunctionDefinition $functionDefinition The function definition object
  *
  * @return string
  */
 protected function generateSkeletonCode($injectNeeded, FunctionDefinition $functionDefinition)
 {
     // first of all: the docblock
     $code = '
     ' . $functionDefinition->getDocBlock() . '
     ' . $functionDefinition->getHeader('definition') . '
     {
         ' . ReservedKeywords::START_LINE_VARIABLE . ' = ' . (int) $functionDefinition->getStartLine() . ';
         ' . ReservedKeywords::END_LINE_VARIABLE . ' = ' . (int) $functionDefinition->getEndLine() . ';
         ' . Placeholders::FUNCTION_BEGIN . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // right after: the "before" join-point
     $code .= Placeholders::BEFORE_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // open the contract context so we are able to avoid endless recursion
     $code .= ReservedKeywords::CONTRACT_CONTEXT . ' = \\AppserverIo\\Doppelgaenger\\ContractContext::open();
         ';
     // Invariant is not needed in private or static functions.
     // Also make sure that there is none in front of the constructor check
     if ($functionDefinition->getVisibility() !== 'private' && !$functionDefinition->isStatic() && $functionDefinition->getName() !== '__construct') {
         $code .= Placeholders::INVARIANT_CALL_START . '
         ';
     }
     $code .= Placeholders::PRECONDITION . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ' . Placeholders::OLD_SETUP . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         ';
     // we will wrap code execution in order to provide a "finally" and "after throwing" placeholder hook.
     // we will also predefine the result as NULL to avoid warnings
     $code .= ReservedKeywords::RESULT . ' = null;
         try {
             ' . Placeholders::AROUND_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
     ';
     // add the second part of the try/catch/finally block
     $code .= '
         } catch (\\Exception ' . ReservedKeywords::THROWN_EXCEPTION_OBJECT . ') {
             ' . Placeholders::AFTERTHROWING_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
             // rethrow the exception
             throw ' . ReservedKeywords::THROWN_EXCEPTION_OBJECT . ';
         } finally {
     ';
     // if we have to inject additional code, we might do so here
     if ($injectNeeded === true) {
         $code .= Placeholders::METHOD_INJECT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     }
     // finish of the block
     $code .= '        ' . Placeholders::AFTER_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         }
     ';
     // now just place all the other placeholder for other filters to come
     $code .= '    ' . Placeholders::POSTCONDITION . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     // Invariant is not needed in private or static functions
     if ($functionDefinition->getVisibility() !== 'private' && !$functionDefinition->isStatic()) {
         $code .= '
         ' . Placeholders::INVARIANT_CALL_END . '
         ';
     }
     // close of the contract context
     $code .= 'if (' . ReservedKeywords::CONTRACT_CONTEXT . ') {
             \\AppserverIo\\Doppelgaenger\\ContractContext::close();
         }
     ';
     // last of all: the "after returning" join-point and the final return from the proxy
     $code .= '    ' . Placeholders::AFTERRETURNING_JOINPOINT . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE . '
         return ' . ReservedKeywords::RESULT . ';
     }
     ';
     return $code;
 }
 /**
  * Returns a FunctionDefinition from a token array.
  *
  * This method will use a set of other methods to parse a token array and retrieve any
  * possible information from it. This information will be entered into a FunctionDefinition object.
  *
  * @param array   $tokens       The token array
  * @param boolean $getRecursive Do we have to get the ancestral conditions as well?
  *
  * @return \AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition
  */
 protected function getDefinitionFromTokens(array $tokens, $getRecursive)
 {
     // First of all we need a new FunctionDefinition to fill
     $functionDefinition = new FunctionDefinition();
     // For our next step we would like to get the doc comment (if any)
     $functionDefinition->setDocBlock($this->getDocBlock($tokens, T_FUNCTION));
     // Get start and end line
     $functionDefinition->setStartLine($this->getStartLine($tokens));
     $functionDefinition->setEndLine($this->getEndLine($tokens));
     // Get the function signature
     $functionDefinition->setIsFinal($this->hasSignatureToken($tokens, T_FINAL, T_FUNCTION));
     $functionDefinition->setIsAbstract($this->hasSignatureToken($tokens, T_ABSTRACT, T_FUNCTION));
     $functionDefinition->setVisibility($this->getFunctionVisibility($tokens));
     $functionDefinition->setIsStatic($this->hasSignatureToken($tokens, T_STATIC, T_FUNCTION));
     $functionDefinition->setName($this->getFunctionName($tokens));
     $functionDefinition->setStructureName($this->currentDefinition->getQualifiedName());
     // Lets also get out parameters
     $functionDefinition->setParameterDefinitions($this->getParameterDefinitionList($tokens));
     // Do we have a private context here? If so we have to tell the annotation parser
     $privateContext = false;
     if ($functionDefinition->getVisibility() === 'private') {
         $privateContext = true;
     }
     // So we got our docBlock, now we can parse the precondition annotations from it
     $annotationParser = new AnnotationParser($this->file, $this->config, $this->tokens, $this->currentDefinition);
     $functionDefinition->setPreconditions($annotationParser->getConditions($functionDefinition->getDocBlock(), Requires::ANNOTATION, $privateContext));
     // get the advices
     $functionDefinition->getPointcutExpressions()->attach($annotationParser->getPointcutExpressions($functionDefinition->getDocBlock(), Joinpoint::TARGET_METHOD, $functionDefinition->getName()));
     // Does this method require the use of our "old" mechanism?
     $functionDefinition->setUsesOld($this->usesKeyword($functionDefinition->getDocBlock(), ReservedKeywords::OLD));
     // We have to get the body of the function, so we can recreate it
     $functionDefinition->setBody($this->getFunctionBody($tokens));
     // So we got our docBlock, now we can parse the postcondition annotations from it
     $functionDefinition->setPostconditions($annotationParser->getConditions($functionDefinition->getDocBlock(), Ensures::ANNOTATION, $privateContext));
     // If we have to parse the definition in a recursive manner, we have to get the parent invariants
     if ($getRecursive === true) {
         // Add all the assertions we might get from ancestral dependencies
         $this->addAncestralAssertions($functionDefinition);
     }
     return $functionDefinition;
 }
 /**
  * Will inject invocation code for a given function into a given piece of code.
  * Invocation code will be the instantiation of a \AppserverIo\Doppelgaenger\Entities\MethodInvocation object
  * as a basic representation of the given function
  *
  * @param string             $bucketData         Reference on the current bucket's data
  * @param FunctionDefinition $functionDefinition Definition of the function to inject invocation code into
  * @param array              $callbackChain      Chain of callbacks which is used to recursively chain calls
  *
  * @return boolean
  */
 protected function injectInvocationCode(&$bucketData, FunctionDefinition $functionDefinition, array $callbackChain)
 {
     // start building up the code
     $code = '
         ' . ReservedKeywords::METHOD_INVOCATION_OBJECT . ' = new \\AppserverIo\\Doppelgaenger\\Entities\\MethodInvocation(
         ';
     // add the original method call to the callback chain so it can be integrated, add it and add the context
     if ($functionDefinition->isStatic()) {
         $contextCode = '__CLASS__';
     } else {
         $contextCode = '$this';
     }
     // iterate the callback chain and build up the code but pop the first element as we will invoke it initially
     unset($callbackChain[0]);
     // empty chain? Add the original function at least
     if (empty($callbackChain)) {
         $callbackChain[] = array($functionDefinition->getStructureName(), $functionDefinition->getName());
     }
     $code .= '    array(';
     foreach ($callbackChain as $callback) {
         // do some brushing up for the structure
         $structure = $callback[0];
         if ($structure === $functionDefinition->getStructureName()) {
             $structure = $contextCode;
         }
         // also brush up the function call to direct to the original
         if ($callback[1] === $functionDefinition->getName()) {
             $callback[1] = $functionDefinition->getName() . ReservedKeywords::ORIGINAL_FUNCTION_SUFFIX;
         }
         $code .= 'array(' . $structure . ', \'' . $callback[1] . '\'),';
     }
     $code .= '),
     ';
     // continue with the access modifiers
     $code .= '        ' . $contextCode . ',
             ' . ($functionDefinition->isAbstract() ? 'true' : 'false') . ',
             ' . ($functionDefinition->isFinal() ? 'true' : 'false') . ',
             ' . ($functionDefinition->isStatic() ? 'true' : 'false') . ',
         ';
     // we have to build up manual parameter collection as func_get_args() only returns copies
     // @see http://php.net/manual/en/function.func-get-args.php
     $parametersCode = '    array(';
     foreach ($functionDefinition->getParameterDefinitions() as $parameterDefinition) {
         $name = $parameterDefinition->name;
         $parametersCode .= '\'' . substr($name, 1) . '\' => ' . $name . ',';
     }
     $parametersCode .= ')';
     $code .= '    \'' . $functionDefinition->getName() . '\',
         ' . $parametersCode . ',
             __CLASS__,
             \'' . $functionDefinition->getVisibility() . '\'
         );';
     // Insert the code
     $placeholder = Placeholders::FUNCTION_BEGIN . $functionDefinition->getName() . Placeholders::PLACEHOLDER_CLOSE;
     $bucketData = str_replace($placeholder, $placeholder . $code, $bucketData);
     return true;
 }