/**
  * 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;
 }