public function toSetter(MetaClass $class, MetaClassProperty $property, MetaClassProperty $holder = null)
    {
        $name = $property->getName();
        $methodName = 'set' . ucfirst($name);
        if ($holder) {
            return <<<EOT

/**
 * @return {$holder->getClass()->getName()}
**/
public function {$methodName}(\${$name})
{
\t\$this->{$holder->getName()}->{$methodName}(\${$name});

\treturn \$this;
}

EOT;
        } else {
            return <<<EOT

/**
 * @return {$class->getName()}
**/
public function {$methodName}(\${$name})
{
\t\$this->{$name} = \${$name};

\treturn \$this;
}

EOT;
        }
        Assert::isUnreachable();
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        $type = $class->getType();
        if ($type) {
            switch ($type->getId()) {
                case MetaClassType::CLASS_ABSTRACT:
                    $abstract = 'abstract ';
                    $notes = 'nothing here yet';
                    break;
                case MetaClassType::CLASS_FINAL:
                    $abstract = 'final ';
                    $notes = 'last chance for customization';
                    break;
                default:
                    throw new WrongStateException('unknown class type');
            }
        } else {
            $abstract = null;
            $notes = 'your brilliant stuff goes here';
        }
        $out .= <<<EOT
{$abstract}class {$class->getName()}DAO extends Auto{$class->getName()}DAO
{
\t// {$notes}
}

EOT;
        return $out . self::getHeel();
    }
    protected static function buildPointers(MetaClass $class)
    {
        $out = null;
        if (!$class->getPattern() instanceof AbstractClassPattern) {
            if ($source = $class->getSourceLink()) {
                $out .= <<<EOT
\tprotected \$linkName =  '{$source}';
\t

EOT;
            }
            if ($class->getIdentifier()->getColumnName() !== 'id') {
                $out .= <<<EOT
public function getIdName()
{
\treturn '{$class->getIdentifier()->getColumnName()}';
}

EOT;
            }
            $out .= <<<EOT
public function getTable()
{
\treturn '{$class->getTableName()}';
}

public function getObjectName()
{
\treturn '{$class->getName()}';
}

public function getSequence()
{
\treturn '{$class->getTableName()}_id';
}
EOT;
        } elseif ($class->getWithInternalProperties()) {
            $out .= <<<EOT
// no get{Table,ObjectName,Sequence} for abstract class
EOT;
        }
        if ($liaisons = $class->getReferencingClasses()) {
            $uncachers = array();
            foreach ($liaisons as $className) {
                $uncachers[] = $className . '::dao()->uncacheLists();';
            }
            $uncachers = implode("\n", $uncachers);
            $out .= <<<EOT


public function uncacheLists()
{
{$uncachers}

return parent::uncacheLists();
}
EOT;
        }
        return $out;
    }
 /**
  * @return EnumClassPattern
  **/
 public function build(MetaClass $class)
 {
     $userFile = ONPHP_META_BUSINESS_DIR . $class->getName() . EXT_CLASS;
     if (MetaConfiguration::me()->isForcedGeneration() || !file_exists($userFile)) {
         $this->dumpFile($userFile, Format::indentize(EnumClassBuilder::build($class)));
     }
     return $this;
 }
    public static function buildContainer(MetaClass $class, MetaClassProperty $holder)
    {
        $out = self::getHead();
        $containerName = $class->getName() . ucfirst($holder->getName()) . 'DAO';
        $out .= 'final class ' . $containerName . ' extends ' . $holder->getRelation()->toString() . 'Linked' . "\n{\n";
        $className = $class->getName();
        $propertyName = strtolower($className[0]) . substr($className, 1);
        $remoteColumnName = $holder->getType()->getClass()->getTableName();
        $out .= <<<EOT
public function __construct({$className} \${$propertyName}, \$lazy = false)
{
\tparent::__construct(
\t\t\${$propertyName},
\t\t{$holder->getType()->getClassName()}::dao(),
\t\t\$lazy
\t);
}

/**
 * @return {$containerName}
**/
public static function create({$className} \${$propertyName}, \$lazy = false)
{
\treturn new self(\${$propertyName}, \$lazy);
}

EOT;
        if ($holder->getRelation()->getId() == MetaRelation::MANY_TO_MANY) {
            $out .= <<<EOT

public function getHelperTable()
{
\treturn '{$class->getTableName()}_{$remoteColumnName}';
}

public function getChildIdField()
{
\treturn '{$remoteColumnName}_id';
}

EOT;
        }
        $out .= <<<EOT

public function getParentIdField()
{
\treturn '{$class->getTableName()}_id';
}

EOT;
        $out .= "}\n";
        $out .= self::getHeel();
        return $out;
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        if ($type = $class->getType()) {
            $typeName = $type->toString() . ' ';
        } else {
            $typeName = null;
        }
        $out .= <<<EOT
{$typeName}class Proto{$class->getName()} extends AutoProto{$class->getName()} {/*_*/}

EOT;
        return $out . self::getHeel();
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        $out .= <<<EOT
abstract class Auto{$class->getName()}DAO extends StorableDAO
{

EOT;
        $pointers = self::buildPointers($class);
        $out .= <<<EOT
{$pointers}
}

EOT;
        return $out . self::getHeel();
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        if ($type = $class->getType()) {
            $type = "{$type->getName()} ";
        } else {
            $type = null;
        }
        $out .= <<<EOT
{$type}class {$class->getName()} extends Enumeration
{
\t// implement me!
}

EOT;
        return $out . self::getHeel();
    }
 private static function doPropertyBuild(MetaClass $class, MetaClassProperty $property, $isNamed)
 {
     if ($parentProperty = $class->isRedefinedProperty($property->getName())) {
         // check wheter property fetch strategy becomes lazy
         if ($parentProperty->getFetchStrategyId() != $property->getFetchStrategyId() && $property->getFetchStrategyId() === FetchStrategy::LAZY) {
             return true;
         }
         return false;
     }
     if ($isNamed && $property->getName() == 'name') {
         return false;
     }
     if ($property->getName() == 'id' && !$property->getClass()->getParent()) {
         return false;
     }
     // do not redefine parent's properties
     if ($property->getClass()->getParent() && array_key_exists($property->getName(), $property->getClass()->getAllParentsProperties())) {
         return false;
     }
     return true;
 }
    public static function build(MetaClass $class)
    {
        if (!$class->hasBuildableParent()) {
            return DictionaryDaoBuilder::build($class);
        } else {
            $parent = $class->getParent();
        }
        if ($class->getParent()->getPattern() instanceof InternalClassPattern) {
            $parentName = 'StorableDAO';
        } else {
            $parentName = $parent->getName() . 'DAO';
        }
        $out = self::getHead();
        $out .= <<<EOT
abstract class Auto{$class->getName()}DAO extends {$parentName}
{

EOT;
        $out .= self::buildPointers($class) . "\n}\n";
        return $out . self::getHeel();
    }
    public function toSetter(MetaClass $class, MetaClassProperty $property, MetaClassProperty $holder = null)
    {
        $name = $property->getName();
        $methodName = 'set' . ucfirst($name);
        if ($holder) {
            return <<<EOT

/**
 * @return {$holder->getClass()->getName()}
**/
public function {$methodName}(\${$name})
{
\t\$this->{$holder->getName()}->{$methodName}(\${$name});

\treturn \$this;
}

EOT;
        } else {
            if ($property->isRequired()) {
                $method = <<<EOT

/**
 * @return {$class->getName()}
**/
public function {$methodName}(\${$name} = false)
{
\t\$this->{$name} = (\${$name} === true);

\treturn \$this;
}

EOT;
            } else {
                $method = <<<EOT

/**
 * @return {$class->getName()}
**/
public function {$methodName}(\${$name} = null)
{
\tAssert::isTernaryBase(\${$name});
\t
\t\$this->{$name} = \${$name};

\treturn \$this;
}

EOT;
            }
        }
        return $method;
    }
 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;
 }
    private static function dumpMetaClass(MetaClass $class)
    {
        $propertyList = $class->getWithInternalProperties();
        $out = <<<EOT
\tprotected function makePropertyList()
\t{

EOT;
        if ($class->hasBuildableParent()) {
            $out .= <<<EOT
\t\treturn
\t\t\tarray_merge(
\t\t\t\tparent::makePropertyList(),
\t\t\t\tarray(

EOT;
            if ($class->getIdentifier()) {
                $propertyList[$class->getIdentifier()->getName()] = $class->getIdentifier();
            }
        } else {
            $out .= <<<EOT
\t\treturn array(

EOT;
        }
        $list = array();
        foreach ($propertyList as $property) {
            $list[] = "'{$property->getName()}' => " . $property->toLightProperty($class)->toString();
        }
        $out .= implode(",\n", $list);
        if ($class->hasBuildableParent()) {
            $out .= "\n)";
        }
        $out .= <<<EOT

\t\t);
\t}
EOT;
        return $out;
    }
 public function toLightProperty(MetaClass $holder)
 {
     $className = null;
     if ($this->getRelationId() == MetaRelation::ONE_TO_MANY || $this->getRelationId() == MetaRelation::MANY_TO_MANY) {
         // collections
         $primitiveName = 'identifierList';
     } elseif ($this->isIdentifier()) {
         if ($this->getType() instanceof IntegerType) {
             $primitiveName = 'integerIdentifier';
             $className = $holder->getName();
         } elseif ($this->getType() instanceof StringType) {
             $primitiveName = 'scalarIdentifier';
             $className = $holder->getName();
         } else {
             $primitiveName = $this->getType()->getPrimitiveName();
         }
     } elseif (!$this->isIdentifier() && !$this->getType()->isGeneric() && $this->getType() instanceof ObjectType) {
         $pattern = $this->getType()->getClass()->getPattern();
         if ($pattern instanceof EnumerationClassPattern) {
             $primitiveName = 'enumeration';
         } elseif ($pattern instanceof EnumClassPattern) {
             $primitiveName = 'enum';
         } elseif ($pattern instanceof DictionaryClassPattern && ($identifier = $this->getType()->getClass()->getIdentifier())) {
             if ($identifier->getType() instanceof IntegerType) {
                 $primitiveName = 'integerIdentifier';
             } elseif ($identifier->getType() instanceof StringType) {
                 $primitiveName = 'scalarIdentifier';
             } else {
                 $primitiveName = $this->getType()->getPrimitiveName();
             }
         } else {
             $primitiveName = $this->getType()->getPrimitiveName();
         }
     } else {
         $primitiveName = $this->getType()->getPrimitiveName();
     }
     $inner = false;
     if ($this->getType() instanceof ObjectType) {
         $className = $this->getType()->getClassName();
         if (!$this->getType()->isGeneric()) {
             $class = $this->getType()->getClass();
             $pattern = $class->getPattern();
             if ($pattern instanceof InternalClassPattern) {
                 $className = $holder->getName();
             }
             if (($pattern instanceof InternalClassPattern || $pattern instanceof ValueObjectPattern) && $className != $holder->getName()) {
                 $inner = true;
             }
         }
     }
     $propertyClassName = $inner ? 'InnerMetaProperty' : 'LightMetaProperty';
     if ($this->getType() instanceof IntegerType) {
         $size = $this->getType()->getSize();
     } elseif ($this->getType() instanceof ObjectType && $this->getRelationId() == MetaRelation::ONE_TO_ONE && ($identifier = $this->getType()->getClass()->getIdentifier()) && $this->getType()->isMeasurable()) {
         $size = $identifier->getType()->getSize();
     } elseif ($this->getType()->isMeasurable()) {
         $size = $this->size;
     } else {
         $size = null;
     }
     return call_user_func_array(array($propertyClassName, 'fill'), array(new $propertyClassName(), $this->getName(), $this->getName() != $this->getRelationColumnName() ? $this->getRelationColumnName() : null, $primitiveName, $className, $size, $this->isRequired(), $this->getType()->isGeneric(), $inner, $this->getRelationId(), $this->getFetchStrategyId()));
 }
 /**
  * @return MetaConfiguration
  **/
 private function checkClassSanity(MetaClass $class, ReflectionClass $info)
 {
     switch ($class->getTypeId()) {
         case null:
             break;
         case MetaClassType::CLASS_ABSTRACT:
             Assert::isTrue($info->isAbstract(), 'class ' . $info->getName() . ' expected to be abstract');
             Assert::isTrue($class->getPattern() instanceof AbstractClassPattern, 'class ' . $info->getName() . ' must use AbstractClassPattern');
             break;
         case MetaClassType::CLASS_FINAL:
             Assert::isTrue($info->isFinal(), 'class ' . $info->getName() . ' expected to be final');
             break;
         case MetaClassType::CLASS_SPOOKED:
         default:
             Assert::isUnreachable();
             break;
     }
     if ($public = $info->getProperties(ReflectionProperty::IS_PUBLIC)) {
         Assert::isUnreachable($class->getName() . ' contains properties with evil visibility:' . "\n" . print_r($public, true));
     }
     return $this;
 }
 /**
  * @return BasePattern
  **/
 protected function buildDao(MetaClass $class)
 {
     $this->dumpFile(ONPHP_META_AUTO_DAO_DIR . 'Auto' . $class->getName() . 'DAO' . EXT_CLASS, Format::indentize(AutoDaoBuilder::build($class)));
     $userFile = ONPHP_META_DAO_DIR . $class->getName() . 'DAO' . EXT_CLASS;
     if (MetaConfiguration::me()->isForcedGeneration() || !file_exists($userFile)) {
         $this->dumpFile($userFile, Format::indentize(DaoBuilder::build($class)));
     }
     return $this;
 }
    public function toDropper(MetaClass $class, MetaClassProperty $property, MetaClassProperty $holder = null)
    {
        if ($property->getRelationId() == MetaRelation::ONE_TO_MANY || $property->getRelationId() == MetaRelation::MANY_TO_MANY) {
            // we don't need dropper in such cases
            return null;
        }
        $name = $property->getName();
        $methodName = 'drop' . ucfirst($name);
        if ($holder) {
            $method = <<<EOT

/**
 * @return {$holder->getClass()->getName()}
**/
public function {$methodName}()
{
\t\$this->{$holder->getName()}->{$methodName}();

\treturn \$this;
}

EOT;
        } else {
            if ($property->getFetchStrategyId() == FetchStrategy::LAZY) {
                $method = <<<EOT

/**
 * @return {$class->getName()}
**/
public function {$methodName}()
{
\t\$this->{$name} = null;
\t\$this->{$name}Id = null;

\treturn \$this;
}

EOT;
            } else {
                $method = <<<EOT

/**
 * @return {$class->getName()}
**/
public function {$methodName}()
{
\t\$this->{$name} = null;

\treturn \$this;
}

EOT;
            }
        }
        return $method;
    }
    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;
    }
    public static function build(MetaClass $class)
    {
        $out = self::getHead();
        if ($type = $class->getType()) {
            $typeName = $type->toString() . ' ';
        } else {
            $typeName = null;
        }
        $interfaces = ' implements Prototyped';
        if ($class->getPattern()->daoExists() && !$class->getPattern() instanceof AbstractClassPattern) {
            $interfaces .= ', DAOConnected';
            $daoName = $class->getName() . 'DAO';
            $dao = <<<EOT
\t/**
\t * @return {$daoName}
\t**/
\tpublic static function dao()
\t{
\t\treturn Singleton::getInstance('{$daoName}');
\t}

EOT;
        } else {
            $dao = null;
        }
        $out .= <<<EOT
{$typeName}class {$class->getName()} extends Auto{$class->getName()}{$interfaces}
{
EOT;
        if (!$type || $type->getId() !== MetaClassType::CLASS_ABSTRACT) {
            $customCreate = null;
            if ($class->getFinalParent()->getPattern() instanceof InternalClassPattern) {
                $parent = $class;
                while ($parent = $parent->getParent()) {
                    $info = new ReflectionClass($parent->getName());
                    if ($info->hasMethod('create') && $info->getMethod('create')->getParameters() > 0) {
                        $customCreate = true;
                        break;
                    }
                }
            }
            if ($customCreate) {
                $creator = $info->getMethod('create');
                $declaration = array();
                foreach ($creator->getParameters() as $parameter) {
                    $declaration[] = '$' . $parameter->getName() . ' = ' . ($parameter->getDefaultValue() ? $parameter->getDefaultValue() : 'null');
                }
                $declaration = implode(', ', $declaration);
                $out .= <<<EOT

\t/**
\t * @return {$class->getName()}
\t**/
\tpublic static function create({$declaration})
\t{
\t\treturn new self({$declaration});
\t}
\t\t
EOT;
            } else {
                $out .= <<<EOT

\t/**
\t * @return {$class->getName()}
\t**/
\tpublic static function create()
\t{
\t\treturn new self;
\t}
\t\t
EOT;
            }
            $protoName = 'Proto' . $class->getName();
            $out .= <<<EOT

{$dao}
\t/**
\t * @return {$protoName}
\t**/
\tpublic static function proto()
\t{
\t\treturn Singleton::getInstance('{$protoName}');
\t}

EOT;
        }
        $out .= <<<EOT

\t// your brilliant stuff goes here
}

EOT;
        return $out . self::getHeel();
    }