/**
  * @param Method $method
  *
  * @return $this
  */
 public function addMethod(Method $method)
 {
     $this->methods[$method->getName()] = $method;
     return $this;
 }
 /**
  * @param $metadataClassName
  */
 public function generate($metadataClassName)
 {
     $oxidPath = $this->oxidPath;
     $this->metadata = array();
     foreach ($this->mappings as $oxidClass => $proxies) {
         foreach ($proxies as $proxyName => $mappings) {
             $phpFile = new PhpFile();
             $phpUse = new PhpUse();
             $moduleId = "aom{$proxyName}";
             $proxyClassName = "{$moduleId}_{$oxidClass}";
             $phpClass = new PhpClass($proxyClassName, $phpUse);
             $phpClass->setExtension("{$proxyClassName}_parent");
             foreach ($this->classesToLoad[$oxidClass][$proxyName] as $moduleClassName => $moduleFile) {
                 $methodName = $this->sanatizeModuleClass($moduleClassName);
                 $instanceProperty = lcfirst($methodName);
                 $property = new Property($instanceProperty);
                 $property->setVisibility(Property::VISIBILITY_PRIVATE);
                 $phpClass->addProperty($property);
                 $method = new Method("get{$methodName}");
                 $method->setVisibility(Method::VISIBILITY_PRIVATE);
                 $body = "if (!class_exists('{$moduleClassName}', false)) {\n";
                 $body .= "include '{$moduleFile}';\n";
                 $body .= "}\n";
                 $body .= "if (null === \$this->{$instanceProperty}) {\n";
                 $body .= "\$this->{$instanceProperty} = new \\{$moduleClassName}();\n";
                 $body .= "}\n";
                 $body .= "return \$this->{$instanceProperty};\n";
                 $method->setBody($body);
                 $phpClass->addMethod($method);
             }
             /** @var Mapping $mapping */
             foreach ($mappings as $mapping) {
                 $body = "\$class = \$this->get" . $this->sanatizeModuleClass($mapping->getModuleClass());
                 $body .= "();\n";
                 $body .= "\$args = func_get_args();\n";
                 $resultVarAssign = "";
                 $resultVarReturn = "";
                 if ($mapping->hasReturn()) {
                     $resultVar = "\$result";
                     $resultVarAssign = "{$resultVar} = ";
                     $resultVarReturn = "return {$resultVar};\n";
                 }
                 if (Annotations\Method::EXECUTE_BEFORE === $mapping->getParentExecution()) {
                     $body .= "{$resultVarAssign}call_user_func_array(array('parent', __FUNCTION__), \$args);\n";
                 }
                 $body .= "{$resultVarAssign}call_user_func_array(array(\$class, '{$mapping->getModuleMethod()}'), \$args);\n";
                 if (Annotations\Method::EXECUTE_AFTER === $mapping->getParentExecution()) {
                     $body .= "{$resultVarAssign}call_user_func_array(array('parent', __FUNCTION__), \$args);\n";
                 }
                 $body .= $resultVarReturn;
                 $methodReflection = new \ReflectionMethod($mapping->getOxidClass(), $mapping->getOxidMethod());
                 $method = new Method($mapping->getOxidMethod(), array(), $body);
                 foreach ($methodReflection->getParameters() as $parameter) {
                     $method->addParameter(new Method\Parameter($parameter->getName(), $parameter->getDefaultValue(), !$parameter->isOptional()));
                 }
                 switch (true) {
                     case $methodReflection->isPublic():
                         $method->setVisibility(Method::VISIBILITY_PUBLIC);
                         break;
                     case $methodReflection->isProtected():
                         $method->setVisibility(Method::VISIBILITY_PROTECTED);
                         break;
                     case $methodReflection->isPrivate():
                         $method->setVisibility(Method::VISIBILITY_PRIVATE);
                         break;
                 }
                 $method->setFinal($methodReflection->isFinal());
                 $method->setAbstract($methodReflection->isAbstract());
                 $phpClass->addMethod($method);
             }
             $vendorPath = $this->modulePath;
             do {
                 $vendorPath = dirname($vendorPath) . '/vendor';
                 if ('/' === $vendorPath || ':' == substr($vendorPath, 1, 1)) {
                     throw new \RuntimeException('Can not locate the vendor directory!');
                 }
             } while (!file_exists($vendorPath));
             $phpAutoloader = new Code("include_once '{$this->vendorPath}/autoload.php';\n");
             $phpFile->addBlock($phpAutoloader);
             $phpFile->addBlock($phpUse);
             $phpFile->addBlock($phpClass);
             $proxyClassExtend = str_replace('_', '/', $proxyClassName) . "/{$proxyClassName}";
             $proxyClassFile = "{$oxidPath}/modules/{$proxyClassExtend}.php";
             if (!file_exists(dirname($proxyClassFile))) {
                 mkdir(dirname($proxyClassFile), 0777, true);
             }
             file_put_contents($proxyClassFile, $phpFile->generate());
             /** @var Metadata $metadataClass */
             $metadataClass = $this->getMetadata($proxyName, $metadataClassName, $moduleId);
             $metadataClass->getExtensions()->addExtension($oxidClass, $proxyClassExtend);
             file_put_contents("{$oxidPath}/modules/{$moduleId}/metadata.php", $metadataClass->generate());
         }
     }
 }