Rajout de doctrine/orm
[zf2.biz/application_blanche.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Tools / EntityGenerator.php
diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php
new file mode 100644 (file)
index 0000000..3e20ee5
--- /dev/null
@@ -0,0 +1,1265 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\ORM\Tools;
+
+use Doctrine\ORM\Mapping\ClassMetadataInfo,
+    Doctrine\Common\Util\Inflector,
+    Doctrine\DBAL\Types\Type;
+
+/**
+ * Generic class used to generate PHP5 entity classes from ClassMetadataInfo instances
+ *
+ *     [php]
+ *     $classes = $em->getClassMetadataFactory()->getAllMetadata();
+ *
+ *     $generator = new \Doctrine\ORM\Tools\EntityGenerator();
+ *     $generator->setGenerateAnnotations(true);
+ *     $generator->setGenerateStubMethods(true);
+ *     $generator->setRegenerateEntityIfExists(false);
+ *     $generator->setUpdateEntityIfExists(true);
+ *     $generator->generate($classes, '/path/to/generate/entities');
+ *
+ *
+ * @link    www.doctrine-project.org
+ * @since   2.0
+ * @author  Benjamin Eberlei <kontakt@beberlei.de>
+ * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
+ * @author  Jonathan Wage <jonwage@gmail.com>
+ * @author  Roman Borschel <roman@code-factory.org>
+ */
+class EntityGenerator
+{
+    /**
+     * Specifies class fields should be protected
+     */
+    const FIELD_VISIBLE_PROTECTED = 'protected';
+
+    /**
+     * Specifies class fields should be private
+     */
+    const FIELD_VISIBLE_PRIVATE = 'private';
+
+    /**
+     * @var bool
+     */
+    private $backupExisting = true;
+
+    /**
+     * The extension to use for written php files
+     *
+     * @var string
+     */
+    private $extension = '.php';
+
+    /**
+     * Whether or not the current ClassMetadataInfo instance is new or old
+     *
+     * @var boolean
+     */
+    private $isNew = true;
+
+    /**
+     * @var array
+     */
+    private $staticReflection = array();
+
+    /**
+     * Number of spaces to use for indention in generated code
+     */
+    private $numSpaces = 4;
+
+    /**
+     * The actual spaces to use for indention
+     *
+     * @var string
+     */
+    private $spaces = '    ';
+
+    /**
+     * The class all generated entities should extend
+     *
+     * @var string
+     */
+    private $classToExtend;
+
+    /**
+     * Whether or not to generation annotations
+     *
+     * @var boolean
+     */
+    private $generateAnnotations = false;
+
+    /**
+     * @var string
+     */
+    private $annotationsPrefix = '';
+
+    /**
+     * Whether or not to generated sub methods
+     *
+     * @var boolean
+     */
+    private $generateEntityStubMethods = false;
+
+    /**
+     * Whether or not to update the entity class if it exists already
+     *
+     * @var boolean
+     */
+    private $updateEntityIfExists = false;
+
+    /**
+     * Whether or not to re-generate entity class if it exists already
+     *
+     * @var boolean
+     */
+    private $regenerateEntityIfExists = false;
+
+    /**
+     * @var boolean
+     */
+    private $fieldVisibility = 'private';
+
+    /**
+     * Hash-map for handle types
+     *
+     * @var array
+     */
+    private $typeAlias = array(
+        Type::DATETIMETZ    => '\DateTime',
+        Type::DATETIME      => '\DateTime',
+        Type::DATE          => '\DateTime',
+        Type::TIME          => '\DateTime',
+        Type::OBJECT        => '\stdClass',
+        Type::BIGINT        => 'integer',
+        Type::SMALLINT      => 'integer',
+        Type::TEXT          => 'string',
+        Type::BLOB          => 'string',
+        Type::DECIMAL       => 'float',
+        Type::JSON_ARRAY    => 'array',
+        Type::SIMPLE_ARRAY  => 'array',
+    );
+
+    /**
+     * @var string
+     */
+    private static $classTemplate =
+'<?php
+
+<namespace>
+
+use Doctrine\ORM\Mapping as ORM;
+
+<entityAnnotation>
+<entityClassName>
+{
+<entityBody>
+}
+';
+
+    /**
+     * @var string
+     */
+    private static $getMethodTemplate =
+'/**
+ * <description>
+ *
+ * @return <variableType>
+ */
+public function <methodName>()
+{
+<spaces>return $this-><fieldName>;
+}';
+
+    /**
+     * @var string
+     */
+    private static $setMethodTemplate =
+'/**
+ * <description>
+ *
+ * @param <variableType>$<variableName>
+ * @return <entity>
+ */
+public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
+{
+<spaces>$this-><fieldName> = $<variableName>;
+
+<spaces>return $this;
+}';
+
+    /**
+     * @var string
+     */
+    private static $addMethodTemplate =
+'/**
+ * <description>
+ *
+ * @param <variableType>$<variableName>
+ * @return <entity>
+ */
+public function <methodName>(<methodTypeHint>$<variableName>)
+{
+<spaces>$this-><fieldName>[] = $<variableName>;
+
+<spaces>return $this;
+}';
+
+    /**
+     * @var string
+     */
+    private static $removeMethodTemplate =
+'/**
+ * <description>
+ *
+ * @param <variableType>$<variableName>
+ */
+public function <methodName>(<methodTypeHint>$<variableName>)
+{
+<spaces>$this-><fieldName>->removeElement($<variableName>);
+}';
+
+    /**
+     * @var string
+     */
+    private static $lifecycleCallbackMethodTemplate =
+'/**
+ * @<name>
+ */
+public function <methodName>()
+{
+<spaces>// Add your code here
+}';
+
+    /**
+     * @var string
+     */
+    private static $constructorMethodTemplate =
+'/**
+ * Constructor
+ */
+public function __construct()
+{
+<spaces><collections>
+}
+';
+
+    public function __construct()
+    {
+        if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
+            $this->annotationsPrefix = 'ORM\\';
+        }
+    }
+
+    /**
+     * Generate and write entity classes for the given array of ClassMetadataInfo instances
+     *
+     * @param array $metadatas
+     * @param string $outputDirectory
+     * @return void
+     */
+    public function generate(array $metadatas, $outputDirectory)
+    {
+        foreach ($metadatas as $metadata) {
+            $this->writeEntityClass($metadata, $outputDirectory);
+        }
+    }
+
+    /**
+     * Generated and write entity class to disk for the given ClassMetadataInfo instance
+     *
+     * @param ClassMetadataInfo $metadata
+     * @param string $outputDirectory
+     * @return void
+     */
+    public function writeEntityClass(ClassMetadataInfo $metadata, $outputDirectory)
+    {
+        $path = $outputDirectory . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $metadata->name) . $this->extension;
+        $dir = dirname($path);
+
+        if ( ! is_dir($dir)) {
+            mkdir($dir, 0777, true);
+        }
+
+        $this->isNew = !file_exists($path) || (file_exists($path) && $this->regenerateEntityIfExists);
+
+        if ( ! $this->isNew) {
+            $this->parseTokensInEntityFile(file_get_contents($path));
+        } else {
+            $this->staticReflection[$metadata->name] = array('properties' => array(), 'methods' => array());
+        }
+
+        if ($this->backupExisting && file_exists($path)) {
+            $backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
+            if (!copy($path, $backupPath)) {
+                throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
+            }
+        }
+
+        // If entity doesn't exist or we're re-generating the entities entirely
+        if ($this->isNew) {
+            file_put_contents($path, $this->generateEntityClass($metadata));
+        // If entity exists and we're allowed to update the entity class
+        } else if ( ! $this->isNew && $this->updateEntityIfExists) {
+            file_put_contents($path, $this->generateUpdatedEntityClass($metadata, $path));
+        }
+    }
+
+    /**
+     * Generate a PHP5 Doctrine 2 entity class from the given ClassMetadataInfo instance
+     *
+     * @param ClassMetadataInfo $metadata
+     * @return string $code
+     */
+    public function generateEntityClass(ClassMetadataInfo $metadata)
+    {
+        $placeHolders = array(
+            '<namespace>',
+            '<entityAnnotation>',
+            '<entityClassName>',
+            '<entityBody>'
+        );
+
+        $replacements = array(
+            $this->generateEntityNamespace($metadata),
+            $this->generateEntityDocBlock($metadata),
+            $this->generateEntityClassName($metadata),
+            $this->generateEntityBody($metadata)
+        );
+
+        $code = str_replace($placeHolders, $replacements, self::$classTemplate);
+        return str_replace('<spaces>', $this->spaces, $code);
+    }
+
+    /**
+     * Generate the updated code for the given ClassMetadataInfo and entity at path
+     *
+     * @param ClassMetadataInfo $metadata
+     * @param string $path
+     * @return string $code;
+     */
+    public function generateUpdatedEntityClass(ClassMetadataInfo $metadata, $path)
+    {
+        $currentCode = file_get_contents($path);
+
+        $body = $this->generateEntityBody($metadata);
+        $body = str_replace('<spaces>', $this->spaces, $body);
+        $last = strrpos($currentCode, '}');
+
+        return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}";
+    }
+
+    /**
+     * Set the number of spaces the exported class should have
+     *
+     * @param integer $numSpaces
+     * @return void
+     */
+    public function setNumSpaces($numSpaces)
+    {
+        $this->spaces = str_repeat(' ', $numSpaces);
+        $this->numSpaces = $numSpaces;
+    }
+
+    /**
+     * Set the extension to use when writing php files to disk
+     *
+     * @param string $extension
+     * @return void
+     */
+    public function setExtension($extension)
+    {
+        $this->extension = $extension;
+    }
+
+    /**
+     * Set the name of the class the generated classes should extend from
+     *
+     * @return void
+     */
+    public function setClassToExtend($classToExtend)
+    {
+        $this->classToExtend = $classToExtend;
+    }
+
+    /**
+     * Set whether or not to generate annotations for the entity
+     *
+     * @param bool $bool
+     * @return void
+     */
+    public function setGenerateAnnotations($bool)
+    {
+        $this->generateAnnotations = $bool;
+    }
+
+    /**
+     * Set the class fields visibility for the entity (can either be private or protected)
+     *
+     * @param bool $bool
+     * @return void
+     */
+    public function setFieldVisibility($visibility)
+    {
+        if ($visibility !== self::FIELD_VISIBLE_PRIVATE && $visibility !== self::FIELD_VISIBLE_PROTECTED) {
+            throw new \InvalidArgumentException('Invalid provided visibilty (only private and protected are allowed): ' . $visibility);
+        }
+
+        $this->fieldVisibility = $visibility;
+    }
+
+    /**
+     * Set an annotation prefix.
+     *
+     * @param string $prefix
+     */
+    public function setAnnotationPrefix($prefix)
+    {
+        $this->annotationsPrefix = $prefix;
+    }
+
+    /**
+     * Set whether or not to try and update the entity if it already exists
+     *
+     * @param bool $bool
+     * @return void
+     */
+    public function setUpdateEntityIfExists($bool)
+    {
+        $this->updateEntityIfExists = $bool;
+    }
+
+    /**
+     * Set whether or not to regenerate the entity if it exists
+     *
+     * @param bool $bool
+     * @return void
+     */
+    public function setRegenerateEntityIfExists($bool)
+    {
+        $this->regenerateEntityIfExists = $bool;
+    }
+
+    /**
+     * Set whether or not to generate stub methods for the entity
+     *
+     * @param bool $bool
+     * @return void
+     */
+    public function setGenerateStubMethods($bool)
+    {
+        $this->generateEntityStubMethods = $bool;
+    }
+
+    /**
+     * Should an existing entity be backed up if it already exists?
+     */
+    public function setBackupExisting($bool)
+    {
+        $this->backupExisting = $bool;
+    }
+
+     /**
+     * @param   string $type
+     * @return  string
+     */
+    private function getType($type)
+    {
+        if (isset($this->typeAlias[$type])) {
+            return $this->typeAlias[$type];
+        }
+
+        return $type;
+    }
+
+    private function generateEntityNamespace(ClassMetadataInfo $metadata)
+    {
+        if ($this->hasNamespace($metadata)) {
+            return 'namespace ' . $this->getNamespace($metadata) .';';
+        }
+    }
+
+    private function generateEntityClassName(ClassMetadataInfo $metadata)
+    {
+        return 'class ' . $this->getClassName($metadata) .
+            ($this->extendsClass() ? ' extends ' . $this->getClassToExtendName() : null);
+    }
+
+    private function generateEntityBody(ClassMetadataInfo $metadata)
+    {
+        $fieldMappingProperties = $this->generateEntityFieldMappingProperties($metadata);
+        $associationMappingProperties = $this->generateEntityAssociationMappingProperties($metadata);
+        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods($metadata) : null;
+        $lifecycleCallbackMethods = $this->generateEntityLifecycleCallbackMethods($metadata);
+
+        $code = array();
+
+        if ($fieldMappingProperties) {
+            $code[] = $fieldMappingProperties;
+        }
+
+        if ($associationMappingProperties) {
+            $code[] = $associationMappingProperties;
+        }
+
+        $code[] = $this->generateEntityConstructor($metadata);
+
+        if ($stubMethods) {
+            $code[] = $stubMethods;
+        }
+
+        if ($lifecycleCallbackMethods) {
+            $code[] = $lifecycleCallbackMethods;
+        }
+
+        return implode("\n", $code);
+    }
+
+    private function generateEntityConstructor(ClassMetadataInfo $metadata)
+    {
+        if ($this->hasMethod('__construct', $metadata)) {
+            return '';
+        }
+
+        $collections = array();
+
+        foreach ($metadata->associationMappings as $mapping) {
+            if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
+                $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
+            }
+        }
+
+        if ($collections) {
+            return $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), self::$constructorMethodTemplate));
+        }
+
+        return '';
+    }
+
+    /**
+     * @todo this won't work if there is a namespace in brackets and a class outside of it.
+     * @param string $src
+     */
+    private function parseTokensInEntityFile($src)
+    {
+        $tokens = token_get_all($src);
+        $lastSeenNamespace = "";
+        $lastSeenClass = false;
+
+        $inNamespace = false;
+        $inClass = false;
+        for ($i = 0; $i < count($tokens); $i++) {
+            $token = $tokens[$i];
+            if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
+                continue;
+            }
+
+            if ($inNamespace) {
+                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
+                    $lastSeenNamespace .= $token[1];
+                } else if (is_string($token) && in_array($token, array(';', '{'))) {
+                    $inNamespace = false;
+                }
+            }
+
+            if ($inClass) {
+                $inClass = false;
+                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
+                $this->staticReflection[$lastSeenClass]['properties'] = array();
+                $this->staticReflection[$lastSeenClass]['methods'] = array();
+            }
+
+            if ($token[0] == T_NAMESPACE) {
+                $lastSeenNamespace = "";
+                $inNamespace = true;
+            } else if ($token[0] == T_CLASS) {
+                $inClass = true;
+            } else if ($token[0] == T_FUNCTION) {
+                if ($tokens[$i+2][0] == T_STRING) {
+                    $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+2][1];
+                } else if ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
+                    $this->staticReflection[$lastSeenClass]['methods'][] = $tokens[$i+3][1];
+                }
+            } else if (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
+                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
+            }
+        }
+    }
+
+    private function hasProperty($property, ClassMetadataInfo $metadata)
+    {
+        if ($this->extendsClass()) {
+            // don't generate property if its already on the base class.
+            $reflClass = new \ReflectionClass($this->getClassToExtend());
+            if ($reflClass->hasProperty($property)) {
+                return true;
+            }
+        }
+
+        return (
+            isset($this->staticReflection[$metadata->name]) &&
+            in_array($property, $this->staticReflection[$metadata->name]['properties'])
+        );
+    }
+
+    private function hasMethod($method, ClassMetadataInfo $metadata)
+    {
+        if ($this->extendsClass()) {
+            // don't generate method if its already on the base class.
+            $reflClass = new \ReflectionClass($this->getClassToExtend());
+            if ($reflClass->hasMethod($method)) {
+                return true;
+            }
+        }
+
+        return (
+            isset($this->staticReflection[$metadata->name]) &&
+            in_array($method, $this->staticReflection[$metadata->name]['methods'])
+        );
+    }
+
+    private function hasNamespace(ClassMetadataInfo $metadata)
+    {
+        return strpos($metadata->name, '\\') ? true : false;
+    }
+
+    private function extendsClass()
+    {
+        return $this->classToExtend ? true : false;
+    }
+
+    private function getClassToExtend()
+    {
+        return $this->classToExtend;
+    }
+
+    private function getClassToExtendName()
+    {
+        $refl = new \ReflectionClass($this->getClassToExtend());
+
+        return '\\' . $refl->getName();
+    }
+
+    private function getClassName(ClassMetadataInfo $metadata)
+    {
+        return ($pos = strrpos($metadata->name, '\\'))
+            ? substr($metadata->name, $pos + 1, strlen($metadata->name)) : $metadata->name;
+    }
+
+    private function getNamespace(ClassMetadataInfo $metadata)
+    {
+        return substr($metadata->name, 0, strrpos($metadata->name, '\\'));
+    }
+
+    private function generateEntityDocBlock(ClassMetadataInfo $metadata)
+    {
+        $lines = array();
+        $lines[] = '/**';
+        $lines[] = ' * '.$metadata->name;
+
+        if ($this->generateAnnotations) {
+            $lines[] = ' *';
+
+            $methods = array(
+                'generateTableAnnotation',
+                'generateInheritanceAnnotation',
+                'generateDiscriminatorColumnAnnotation',
+                'generateDiscriminatorMapAnnotation'
+            );
+
+            foreach ($methods as $method) {
+                if ($code = $this->$method($metadata)) {
+                    $lines[] = ' * ' . $code;
+                }
+            }
+
+            if ($metadata->isMappedSuperclass) {
+                $lines[] = ' * @' . $this->annotationsPrefix . 'MappedSuperClass';
+            } else {
+                $lines[] = ' * @' . $this->annotationsPrefix . 'Entity';
+            }
+
+            if ($metadata->customRepositoryClassName) {
+                $lines[count($lines) - 1] .= '(repositoryClass="' . $metadata->customRepositoryClassName . '")';
+            }
+
+            if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
+                $lines[] = ' * @' . $this->annotationsPrefix . 'HasLifecycleCallbacks';
+            }
+        }
+
+        $lines[] = ' */';
+
+        return implode("\n", $lines);
+    }
+
+    private function generateTableAnnotation($metadata)
+    {
+        $table = array();
+
+        if (isset($metadata->table['schema'])) {
+            $table[] = 'schema="' . $metadata->table['schema'] . '"';
+        }
+
+        if (isset($metadata->table['name'])) {
+            $table[] = 'name="' . $metadata->table['name'] . '"';
+        }
+
+        if (isset($metadata->table['uniqueConstraints']) && $metadata->table['uniqueConstraints']) {
+            $constraints = $this->generateTableConstraints('UniqueConstraint', $metadata->table['uniqueConstraints']);
+            $table[] = 'uniqueConstraints={' . $constraints . '}';
+        }
+
+        if (isset($metadata->table['indexes']) && $metadata->table['indexes']) {
+            $constraints = $this->generateTableConstraints('Index', $metadata->table['indexes']);
+            $table[] = 'indexes={' . $constraints . '}';
+        }
+
+        return '@' . $this->annotationsPrefix . 'Table(' . implode(', ', $table) . ')';
+    }
+
+    private function generateTableConstraints($constraintName, $constraints)
+    {
+        $annotations = array();
+        foreach ($constraints as $name => $constraint) {
+            $columns = array();
+            foreach ($constraint['columns'] as $column) {
+                $columns[] = '"' . $column . '"';
+            }
+            $annotations[] = '@' . $this->annotationsPrefix . $constraintName . '(name="' . $name . '", columns={' . implode(', ', $columns) . '})';
+        }
+        return implode(', ', $annotations);
+    }
+
+    private function generateInheritanceAnnotation($metadata)
+    {
+        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
+            return '@' . $this->annotationsPrefix . 'InheritanceType("'.$this->getInheritanceTypeString($metadata->inheritanceType).'")';
+        }
+    }
+
+    private function generateDiscriminatorColumnAnnotation($metadata)
+    {
+        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
+            $discrColumn = $metadata->discriminatorValue;
+            $columnDefinition = 'name="' . $discrColumn['name']
+                . '", type="' . $discrColumn['type']
+                . '", length=' . $discrColumn['length'];
+
+            return '@' . $this->annotationsPrefix . 'DiscriminatorColumn(' . $columnDefinition . ')';
+        }
+    }
+
+    private function generateDiscriminatorMapAnnotation($metadata)
+    {
+        if ($metadata->inheritanceType != ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
+            $inheritanceClassMap = array();
+
+            foreach ($metadata->discriminatorMap as $type => $class) {
+                $inheritanceClassMap[] .= '"' . $type . '" = "' . $class . '"';
+            }
+
+            return '@' . $this->annotationsPrefix . 'DiscriminatorMap({' . implode(', ', $inheritanceClassMap) . '})';
+        }
+    }
+
+    private function generateEntityStubMethods(ClassMetadataInfo $metadata)
+    {
+        $methods = array();
+
+        foreach ($metadata->fieldMappings as $fieldMapping) {
+            if ( ! isset($fieldMapping['id']) || ! $fieldMapping['id'] || $metadata->generatorType == ClassMetadataInfo::GENERATOR_TYPE_NONE) {
+                if ($code = $this->generateEntityStubMethod($metadata, 'set', $fieldMapping['fieldName'], $fieldMapping['type'])) {
+                    $methods[] = $code;
+                }
+            }
+
+            if ($code = $this->generateEntityStubMethod($metadata, 'get', $fieldMapping['fieldName'], $fieldMapping['type'])) {
+                $methods[] = $code;
+            }
+        }
+
+        foreach ($metadata->associationMappings as $associationMapping) {
+            if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
+                $nullable = $this->isAssociationIsNullable($associationMapping) ? 'null' : null;
+                if ($code = $this->generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
+                    $methods[] = $code;
+                }
+                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
+                    $methods[] = $code;
+                }
+            } else if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
+                if ($code = $this->generateEntityStubMethod($metadata, 'add', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
+                    $methods[] = $code;
+                }
+                if ($code = $this->generateEntityStubMethod($metadata, 'remove', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
+                    $methods[] = $code;
+                }
+                if ($code = $this->generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], 'Doctrine\Common\Collections\Collection')) {
+                    $methods[] = $code;
+                }
+            }
+        }
+
+        return implode("\n\n", $methods);
+    }
+
+    private function isAssociationIsNullable($associationMapping)
+    {
+        if (isset($associationMapping['id']) && $associationMapping['id']) {
+            return false;
+        }
+        if (isset($associationMapping['joinColumns'])) {
+            $joinColumns = $associationMapping['joinColumns'];
+        } else {
+            //@todo thereis no way to retreive targetEntity metadata
+            $joinColumns = array();
+        }
+        foreach ($joinColumns as $joinColumn) {
+            if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private function generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
+    {
+        if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
+            $methods = array();
+
+            foreach ($metadata->lifecycleCallbacks as $name => $callbacks) {
+                foreach ($callbacks as $callback) {
+                    if ($code = $this->generateLifecycleCallbackMethod($name, $callback, $metadata)) {
+                        $methods[] = $code;
+                    }
+                }
+            }
+
+            return implode("\n\n", $methods);
+        }
+
+        return "";
+    }
+
+    private function generateEntityAssociationMappingProperties(ClassMetadataInfo $metadata)
+    {
+        $lines = array();
+
+        foreach ($metadata->associationMappings as $associationMapping) {
+            if ($this->hasProperty($associationMapping['fieldName'], $metadata)) {
+                continue;
+            }
+
+            $lines[] = $this->generateAssociationMappingPropertyDocBlock($associationMapping, $metadata);
+            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $associationMapping['fieldName']
+                     . ($associationMapping['type'] == 'manyToMany' ? ' = array()' : null) . ";\n";
+        }
+
+        return implode("\n", $lines);
+    }
+
+    private function generateEntityFieldMappingProperties(ClassMetadataInfo $metadata)
+    {
+        $lines = array();
+
+        foreach ($metadata->fieldMappings as $fieldMapping) {
+            if ($this->hasProperty($fieldMapping['fieldName'], $metadata) ||
+                $metadata->isInheritedField($fieldMapping['fieldName'])) {
+                continue;
+            }
+
+            $lines[] = $this->generateFieldMappingPropertyDocBlock($fieldMapping, $metadata);
+            $lines[] = $this->spaces . $this->fieldVisibility . ' $' . $fieldMapping['fieldName']
+                     . (isset($fieldMapping['default']) ? ' = ' . var_export($fieldMapping['default'], true) : null) . ";\n";
+        }
+
+        return implode("\n", $lines);
+    }
+
+    private function generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null,  $defaultValue = null)
+    {
+        $methodName = $type . Inflector::classify($fieldName);
+        if (in_array($type, array("add", "remove")) && substr($methodName, -1) == "s") {
+            $methodName = substr($methodName, 0, -1);
+        }
+
+        if ($this->hasMethod($methodName, $metadata)) {
+            return;
+        }
+        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
+
+        $var = sprintf('%sMethodTemplate', $type);
+        $template = self::$$var;
+
+        $types          = Type::getTypesMap();
+        $variableType   = $typeHint ? $this->getType($typeHint) . ' ' : null;
+        $methodTypeHint = $typeHint && ! isset($types[$typeHint]) ? '\\' . $typeHint . ' ' : null;
+
+        $replacements = array(
+          '<description>'       => ucfirst($type) . ' ' . $fieldName,
+          '<methodTypeHint>'    => $methodTypeHint,
+          '<variableType>'      => $variableType,
+          '<variableName>'      => Inflector::camelize($fieldName),
+          '<methodName>'        => $methodName,
+          '<fieldName>'         => $fieldName,
+          '<variableDefault>'   => ($defaultValue !== null ) ? (' = '.$defaultValue) : '',
+          '<entity>'            => $this->getClassName($metadata)
+        );
+
+        $method = str_replace(
+            array_keys($replacements),
+            array_values($replacements),
+            $template
+        );
+
+        return $this->prefixCodeWithSpaces($method);
+    }
+
+    private function generateLifecycleCallbackMethod($name, $methodName, $metadata)
+    {
+        if ($this->hasMethod($methodName, $metadata)) {
+            return;
+        }
+        $this->staticReflection[$metadata->name]['methods'][] = $methodName;
+
+        $replacements = array(
+            '<name>'        => $this->annotationsPrefix . ucfirst($name),
+            '<methodName>'  => $methodName,
+        );
+
+        $method = str_replace(
+            array_keys($replacements),
+            array_values($replacements),
+            self::$lifecycleCallbackMethodTemplate
+        );
+
+        return $this->prefixCodeWithSpaces($method);
+    }
+
+    private function generateJoinColumnAnnotation(array $joinColumn)
+    {
+        $joinColumnAnnot = array();
+
+        if (isset($joinColumn['name'])) {
+            $joinColumnAnnot[] = 'name="' . $joinColumn['name'] . '"';
+        }
+
+        if (isset($joinColumn['referencedColumnName'])) {
+            $joinColumnAnnot[] = 'referencedColumnName="' . $joinColumn['referencedColumnName'] . '"';
+        }
+
+        if (isset($joinColumn['unique']) && $joinColumn['unique']) {
+            $joinColumnAnnot[] = 'unique=' . ($joinColumn['unique'] ? 'true' : 'false');
+        }
+
+        if (isset($joinColumn['nullable'])) {
+            $joinColumnAnnot[] = 'nullable=' . ($joinColumn['nullable'] ? 'true' : 'false');
+        }
+
+        if (isset($joinColumn['onDelete'])) {
+            $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
+        }
+
+        if (isset($joinColumn['columnDefinition'])) {
+            $joinColumnAnnot[] = 'columnDefinition="' . $joinColumn['columnDefinition'] . '"';
+        }
+
+        return '@' . $this->annotationsPrefix . 'JoinColumn(' . implode(', ', $joinColumnAnnot) . ')';
+    }
+
+    private function generateAssociationMappingPropertyDocBlock(array $associationMapping, ClassMetadataInfo $metadata)
+    {
+        $lines = array();
+        $lines[] = $this->spaces . '/**';
+
+        if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
+            $lines[] = $this->spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
+        } else {
+            $lines[] = $this->spaces . ' * @var ' . $associationMapping['targetEntity'];
+        }
+
+        if ($this->generateAnnotations) {
+            $lines[] = $this->spaces . ' *';
+
+            if (isset($associationMapping['id']) && $associationMapping['id']) {
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
+
+                if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
+                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
+                }
+            }
+
+            $type = null;
+            switch ($associationMapping['type']) {
+                case ClassMetadataInfo::ONE_TO_ONE:
+                    $type = 'OneToOne';
+                    break;
+                case ClassMetadataInfo::MANY_TO_ONE:
+                    $type = 'ManyToOne';
+                    break;
+                case ClassMetadataInfo::ONE_TO_MANY:
+                    $type = 'OneToMany';
+                    break;
+                case ClassMetadataInfo::MANY_TO_MANY:
+                    $type = 'ManyToMany';
+                    break;
+            }
+            $typeOptions = array();
+
+            if (isset($associationMapping['targetEntity'])) {
+                $typeOptions[] = 'targetEntity="' . $associationMapping['targetEntity'] . '"';
+            }
+
+            if (isset($associationMapping['inversedBy'])) {
+                $typeOptions[] = 'inversedBy="' . $associationMapping['inversedBy'] . '"';
+            }
+
+            if (isset($associationMapping['mappedBy'])) {
+                $typeOptions[] = 'mappedBy="' . $associationMapping['mappedBy'] . '"';
+            }
+
+            if ($associationMapping['cascade']) {
+                $cascades = array();
+
+                if ($associationMapping['isCascadePersist']) $cascades[] = '"persist"';
+                if ($associationMapping['isCascadeRemove']) $cascades[] = '"remove"';
+                if ($associationMapping['isCascadeDetach']) $cascades[] = '"detach"';
+                if ($associationMapping['isCascadeMerge']) $cascades[] = '"merge"';
+                if ($associationMapping['isCascadeRefresh']) $cascades[] = '"refresh"';
+
+                $typeOptions[] = 'cascade={' . implode(',', $cascades) . '}';
+            }
+
+            if (isset($associationMapping['orphanRemoval']) && $associationMapping['orphanRemoval']) {
+                $typeOptions[] = 'orphanRemoval=' . ($associationMapping['orphanRemoval'] ? 'true' : 'false');
+            }
+
+            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . '' . $type . '(' . implode(', ', $typeOptions) . ')';
+
+            if (isset($associationMapping['joinColumns']) && $associationMapping['joinColumns']) {
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinColumns({';
+
+                $joinColumnsLines = array();
+
+                foreach ($associationMapping['joinColumns'] as $joinColumn) {
+                    if ($joinColumnAnnot = $this->generateJoinColumnAnnotation($joinColumn)) {
+                        $joinColumnsLines[] = $this->spaces . ' *   ' . $joinColumnAnnot;
+                    }
+                }
+
+                $lines[] = implode(",\n", $joinColumnsLines);
+                $lines[] = $this->spaces . ' * })';
+            }
+
+            if (isset($associationMapping['joinTable']) && $associationMapping['joinTable']) {
+                $joinTable = array();
+                $joinTable[] = 'name="' . $associationMapping['joinTable']['name'] . '"';
+
+                if (isset($associationMapping['joinTable']['schema'])) {
+                    $joinTable[] = 'schema="' . $associationMapping['joinTable']['schema'] . '"';
+                }
+
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'JoinTable(' . implode(', ', $joinTable) . ',';
+                $lines[] = $this->spaces . ' *   joinColumns={';
+
+                foreach ($associationMapping['joinTable']['joinColumns'] as $joinColumn) {
+                    $lines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
+                }
+
+                $lines[] = $this->spaces . ' *   },';
+                $lines[] = $this->spaces . ' *   inverseJoinColumns={';
+
+                foreach ($associationMapping['joinTable']['inverseJoinColumns'] as $joinColumn) {
+                    $lines[] = $this->spaces . ' *     ' . $this->generateJoinColumnAnnotation($joinColumn);
+                }
+
+                $lines[] = $this->spaces . ' *   }';
+                $lines[] = $this->spaces . ' * )';
+            }
+
+            if (isset($associationMapping['orderBy'])) {
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'OrderBy({';
+
+                foreach ($associationMapping['orderBy'] as $name => $direction) {
+                    $lines[] = $this->spaces . ' *     "' . $name . '"="' . $direction . '",';
+                }
+
+                $lines[count($lines) - 1] = substr($lines[count($lines) - 1], 0, strlen($lines[count($lines) - 1]) - 1);
+                $lines[] = $this->spaces . ' * })';
+            }
+        }
+
+        $lines[] = $this->spaces . ' */';
+
+        return implode("\n", $lines);
+    }
+
+    private function generateFieldMappingPropertyDocBlock(array $fieldMapping, ClassMetadataInfo $metadata)
+    {
+        $lines = array();
+        $lines[] = $this->spaces . '/**';
+        $lines[] = $this->spaces . ' * @var ' . $this->getType($fieldMapping['type']) . ' $' . $fieldMapping['fieldName'];
+
+        if ($this->generateAnnotations) {
+            $lines[] = $this->spaces . ' *';
+
+            $column = array();
+            if (isset($fieldMapping['columnName'])) {
+                $column[] = 'name="' . $fieldMapping['columnName'] . '"';
+            }
+
+            if (isset($fieldMapping['type'])) {
+                $column[] = 'type="' . $fieldMapping['type'] . '"';
+            }
+
+            if (isset($fieldMapping['length'])) {
+                $column[] = 'length=' . $fieldMapping['length'];
+            }
+
+            if (isset($fieldMapping['precision'])) {
+                $column[] = 'precision=' .  $fieldMapping['precision'];
+            }
+
+            if (isset($fieldMapping['scale'])) {
+                $column[] = 'scale=' . $fieldMapping['scale'];
+            }
+
+            if (isset($fieldMapping['nullable'])) {
+                $column[] = 'nullable=' .  var_export($fieldMapping['nullable'], true);
+            }
+
+            if (isset($fieldMapping['columnDefinition'])) {
+                $column[] = 'columnDefinition="' . $fieldMapping['columnDefinition'] . '"';
+            }
+
+            if (isset($fieldMapping['unique'])) {
+                $column[] = 'unique=' . var_export($fieldMapping['unique'], true);
+            }
+
+            $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Column(' . implode(', ', $column) . ')';
+
+            if (isset($fieldMapping['id']) && $fieldMapping['id']) {
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Id';
+
+                if ($generatorType = $this->getIdGeneratorTypeString($metadata->generatorType)) {
+                    $lines[] = $this->spaces.' * @' . $this->annotationsPrefix . 'GeneratedValue(strategy="' . $generatorType . '")';
+                }
+
+                if ($metadata->sequenceGeneratorDefinition) {
+                    $sequenceGenerator = array();
+
+                    if (isset($metadata->sequenceGeneratorDefinition['sequenceName'])) {
+                        $sequenceGenerator[] = 'sequenceName="' . $metadata->sequenceGeneratorDefinition['sequenceName'] . '"';
+                    }
+
+                    if (isset($metadata->sequenceGeneratorDefinition['allocationSize'])) {
+                        $sequenceGenerator[] = 'allocationSize=' . $metadata->sequenceGeneratorDefinition['allocationSize'];
+                    }
+
+                    if (isset($metadata->sequenceGeneratorDefinition['initialValue'])) {
+                        $sequenceGenerator[] = 'initialValue=' . $metadata->sequenceGeneratorDefinition['initialValue'];
+                    }
+
+                    $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'SequenceGenerator(' . implode(', ', $sequenceGenerator) . ')';
+                }
+            }
+
+            if (isset($fieldMapping['version']) && $fieldMapping['version']) {
+                $lines[] = $this->spaces . ' * @' . $this->annotationsPrefix . 'Version';
+            }
+        }
+
+        $lines[] = $this->spaces . ' */';
+
+        return implode("\n", $lines);
+    }
+
+    private function prefixCodeWithSpaces($code, $num = 1)
+    {
+        $lines = explode("\n", $code);
+
+        foreach ($lines as $key => $value) {
+            $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
+        }
+
+        return implode("\n", $lines);
+    }
+
+    private function getInheritanceTypeString($type)
+    {
+        switch ($type) {
+            case ClassMetadataInfo::INHERITANCE_TYPE_NONE:
+                return 'NONE';
+
+            case ClassMetadataInfo::INHERITANCE_TYPE_JOINED:
+                return 'JOINED';
+
+            case ClassMetadataInfo::INHERITANCE_TYPE_SINGLE_TABLE:
+                return 'SINGLE_TABLE';
+
+            case ClassMetadataInfo::INHERITANCE_TYPE_TABLE_PER_CLASS:
+                return 'PER_CLASS';
+
+            default:
+                throw new \InvalidArgumentException('Invalid provided InheritanceType: ' . $type);
+        }
+    }
+
+    private function getChangeTrackingPolicyString($policy)
+    {
+        switch ($policy) {
+            case ClassMetadataInfo::CHANGETRACKING_DEFERRED_IMPLICIT:
+                return 'DEFERRED_IMPLICIT';
+
+            case ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT:
+                return 'DEFERRED_EXPLICIT';
+
+            case ClassMetadataInfo::CHANGETRACKING_NOTIFY:
+                return 'NOTIFY';
+
+            default:
+                throw new \InvalidArgumentException('Invalid provided ChangeTrackingPolicy: ' . $policy);
+        }
+    }
+
+    private function getIdGeneratorTypeString($type)
+    {
+        switch ($type) {
+            case ClassMetadataInfo::GENERATOR_TYPE_AUTO:
+                return 'AUTO';
+
+            case ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE:
+                return 'SEQUENCE';
+
+            case ClassMetadataInfo::GENERATOR_TYPE_TABLE:
+                return 'TABLE';
+
+            case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY:
+                return 'IDENTITY';
+
+            case ClassMetadataInfo::GENERATOR_TYPE_NONE:
+                return 'NONE';
+
+            default:
+                throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type);
+        }
+    }
+}