public static function generate(MetaClass $class)
 {
     if (!$class->doBuild()) {
         return null;
     }
     $out = '';
     $out .= "class " . $class->getName();
     $out .= " {\n";
     foreach ($class->getProperties() as $property) {
         $out .= "+get" . ucfirst($property->getName()) . "()\n";
     }
     $out .= "}\n";
     if ($class->getParent()) {
         $out .= $class->getParent()->getName() . " <|-- " . $class->getName() . "\n";
     }
     $out .= "\n";
     return $out;
 }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        $out .= "abstract class Auto{$class->getName()}";
        $isNamed = false;
        if ($parent = $class->getParent()) {
            $out .= " extends {$parent->getName()}";
        } elseif ($class->getPattern() instanceof DictionaryClassPattern && $class->hasProperty('name')) {
            $out .= " extends NamedObject";
            $isNamed = true;
        } elseif (!$class->getPattern() instanceof ValueObjectPattern) {
            $out .= " extends IdentifiableObject";
        }
        if ($interfaces = $class->getInterfaces()) {
            $out .= ' implements ' . implode(', ', $interfaces);
        }
        $out .= "\n{\n";
        foreach ($class->getProperties() as $property) {
            if (!self::doPropertyBuild($class, $property, $isNamed)) {
                continue;
            }
            $out .= "protected \${$property->getName()} = " . "{$property->getType()->getDeclaration()};\n";
            if ($property->getFetchStrategyId() == FetchStrategy::LAZY) {
                $out .= "protected \${$property->getName()}Id = null;\n";
            }
        }
        $valueObjects = array();
        foreach ($class->getProperties() as $property) {
            if ($property->getType() instanceof ObjectType && !$property->getType()->isGeneric() && $property->getType()->getClass()->getPattern() instanceof ValueObjectPattern) {
                $valueObjects[$property->getName()] = $property->getType()->getClassName();
            }
        }
        if ($valueObjects) {
            $out .= <<<EOT

public function __construct()
{

EOT;
            foreach ($valueObjects as $propertyName => $className) {
                $out .= "\$this->{$propertyName} = new {$className}();\n";
            }
            $out .= "}\n";
        }
        foreach ($class->getProperties() as $property) {
            if (!self::doPropertyBuild($class, $property, $isNamed)) {
                continue;
            }
            $out .= $property->toMethods($class);
        }
        $out .= "}\n";
        $out .= self::getHeel();
        return $out;
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        $parent = $class->getParent();
        if ($class->hasBuildableParent()) {
            $parentName = 'Proto' . $parent->getName();
        } else {
            $parentName = 'AbstractProtoClass';
        }
        $out .= <<<EOT
abstract class AutoProto{$class->getName()} extends {$parentName}
{
EOT;
        $classDump = self::dumpMetaClass($class);
        $out .= <<<EOT

{$classDump}
}

EOT;
        return $out . self::getHeel();
    }
 /**
  * @return MetaConfiguration
  **/
 private function checkSanity(MetaClass $class)
 {
     if ((!$class->getParent() || $class->getFinalParent()->getPattern() instanceof InternalClassPattern) && !$class->getPattern() instanceof ValueObjectPattern && !$class->getPattern() instanceof InternalClassPattern) {
         Assert::isTrue($class->getIdentifier() !== null, 'only value objects can live without identifiers. ' . 'do not use them anyway (' . $class->getName() . ')');
     }
     if ($class->getType() && $class->getTypeId() == MetaClassType::CLASS_SPOOKED) {
         Assert::isFalse(count($class->getProperties()) > 1, 'spooked classes must have only identifier: ' . $class->getName());
         Assert::isTrue($class->getPattern() instanceof SpookedClassPattern || $class->getPattern() instanceof SpookedEnumerationPattern, 'spooked classes must use spooked patterns only: ' . $class->getName());
     }
     foreach ($class->getProperties() as $property) {
         if (!$property->getType()->isGeneric() && $property->getType() instanceof ObjectType && $property->getType()->getClass()->getPattern() instanceof ValueObjectPattern) {
             Assert::isTrue($property->isRequired(), 'optional value object is not supported:' . $property->getName() . ' @ ' . $class->getName());
             Assert::isTrue($property->getRelationId() == MetaRelation::ONE_TO_ONE, 'value objects must have OneToOne relation: ' . $property->getName() . ' @ ' . $class->getName());
         } elseif ($property->getFetchStrategyId() == FetchStrategy::LAZY && $property->getType()->isGeneric()) {
             throw new WrongArgumentException('lazy one-to-one is supported only for ' . 'non-generic object types ' . '(' . $property->getName() . ' @ ' . $class->getName() . ')');
         }
     }
     return $this;
 }
    public static function buildRelations(MetaClass $class)
    {
        $out = null;
        $knownJunctions = array();
        foreach ($class->getAllProperties() as $property) {
            if ($relation = $property->getRelation()) {
                $foreignClass = $property->getType()->getClass();
                if ($relation->getId() == MetaRelation::ONE_TO_MANY || !$foreignClass->getPattern()->tableExists() || $class->getParent()) {
                    continue;
                } elseif ($relation->getId() == MetaRelation::MANY_TO_MANY) {
                    $tableName = $class->getTableName() . '_' . $foreignClass->getTableName();
                    if (isset($knownJunctions[$tableName])) {
                        continue;
                    } else {
                        $knownJunctions[$tableName] = true;
                    }
                    $foreignPropery = clone $foreignClass->getIdentifier();
                    $name = $class->getName();
                    $name = strtolower($name[0]) . substr($name, 1);
                    $name .= 'Id';
                    $foreignPropery->setName($name)->setColumnName($foreignPropery->getConvertedName())->setIdentifier(false);
                    // we don't want any garbage in such tables
                    $property = clone $property;
                    $property->required();
                    // prevent name collisions
                    if ($property->getRelationColumnName() == $foreignPropery->getColumnName()) {
                        $foreignPropery->setColumnName($class->getTableName() . '_' . $property->getConvertedName() . '_id');
                    }
                    $out .= <<<EOT
\$schema->
\taddTable(
\t\tDBTable::create('{$tableName}')->
\t\t{$property->toColumn()}->
\t\t{$foreignPropery->toColumn()}->
\t\taddUniques('{$property->getRelationColumnName()}', '{$foreignPropery->getColumnName()}')
\t);


EOT;
                } else {
                    $sourceTable = $class->getTableName();
                    $sourceColumn = $property->getRelationColumnName();
                    $targetTable = $foreignClass->getTableName();
                    $targetColumn = $foreignClass->getIdentifier()->getColumnName();
                    $out .= <<<EOT
// {$sourceTable}.{$sourceColumn} -> {$targetTable}.{$targetColumn}
\$schema->
\tgetTableByName('{$sourceTable}')->
\t\tgetColumnByName('{$sourceColumn}')->
\t\t\tsetReference(
\t\t\t\t\$schema->
\t\t\t\t\tgetTableByName('{$targetTable}')->
\t\t\t\t\tgetColumnByName('{$targetColumn}'),
\t\t\t\tForeignChangeAction::restrict(),
\t\t\t\tForeignChangeAction::cascade()
\t\t\t);


EOT;
                }
            }
        }
        return $out;
    }