diff Lib/Schema.pm @ 0:03e58a454b20

Создан репозитарий
author Sergey
date Tue, 14 Jul 2009 12:54:37 +0400
parents
children 16ada169ca75
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/Schema.pm	Tue Jul 14 12:54:37 2009 +0400
@@ -0,0 +1,748 @@
+package Schema;
+package Schema::TypeName;
+package Schema::Type;
+package Schema::Template;
+package Schema::TemplateSpec;
+package Schema::Member;
+package Schema::Property;
+
+package Schema::TypeName;
+use Common;
+
+#our @ISA = qw(Object);
+
+# ìîæíî îïòèìèçèðîâàòü ïðîèçâîäèòåëüíîñòü, ñîçäàâàÿ îáúåêò ñêàëàÿð äëÿ ïðîñòûõ
+# èìåí è îáõåêò õåø äëÿ ñïåöèàëèçàöèé
+# ñäåëàíî
+
+sub new {
+    my $class = shift;
+    my $this;
+    
+    my $name = shift;
+    my @list = map { ref $_ eq 'Schema::TypeName' ? $_ : new Schema::TypeName($_) } @_;
+    
+    die new Exception('TypeName soud be a simple identifier',$name) if not $name =~ /^\w+$/;
+    
+    if (@list) {
+        $this = bless {}, $class;
+        $this->{Name} = $name;
+        $this->{TemplateList} = \@list if @list;
+    } else {
+        $this = bless \$name, $class;
+    }
+    
+    return $this;
+}
+
+sub Name {
+    my $this = shift;
+    return (UNIVERSAL::isa($this,'HASH') ? $this->{Name} : $$this);
+}
+
+sub Simple {
+    return $_[0]->Name;
+}
+
+# ñïèñîê ïàðàìåòðîâ òèïà
+sub TemplateList {
+    my $this = shift;
+    return (UNIVERSAL::isa($this,'HASH') ? (wantarray ? @{$this->{TemplateList}} : $this->{TemplateList} ) : (wantarray ? return () : undef));
+}
+
+# èìÿ òèïà ÿâëÿåòñÿ èìåíåì øàáëîíà
+sub isTemplateSpec {
+    my $this = shift;
+    return( UNIVERSAL::isa($this,'HASH') ? 1 : 0 );
+}
+
+sub CanonicalName {
+    my $this = shift;
+    
+    if (UNIVERSAL::isa($this,'HASH')) {
+        if (my $result = $this->{SavedCanonicalName}) {
+            $result;
+        } else {
+            $result = $this->{Name};
+            $result .= '@'. join('#',map {ref $_ eq __PACKAGE__ ? $_->CanonicalName : $_} @{$this->{TemplateList}}) . '@@';
+            $this->{SavedCanonicalName} = $result;
+        }
+    } else {
+        $$this;
+    }
+}
+
+sub Canonical {
+    return $_[0]->CanonicalName;
+}
+
+# Íå ðåãèñòðèðóåò âíîâü ñîçäàííûõ òèïîâ â òàáëèöå
+# Ýòî èç-çà ñëó÷àÿ, êîãäà:
+# MyClass { Hash<int> my_map; }, òîåñòü ïîëó÷åííûé òèï Hahs<int> óæå ñïåöèàëèçèðîâàí è îí áóäåò ñðàçó èíñòàíòèíîðîâàí
+# DoNotCreate äëÿ ñïåöèàëèçàöèè øàáëîíà òîëüêî ñóùåñòâóþùèìè òèïàìè
+sub Resolve {
+    my ($this,$TypeTable,$DoNotCreate) = @_;
+    
+    if (my $type = $TypeTable->ResolveType($this,$DoNotCreate)) {
+        # ïðåäïîëàãàåòñÿ, ÷òî ñõåìà àâòîìàòè÷åñêè ñîçäàåò ññûëêè âïåðåä íà íåîïðåäåëåííûå ïðîñòûå òèïû
+        return $type;
+    } else {
+        if ($this->isTemplateSpec) {
+            return new Schema::TemplateSpec($this->Name,map {ref $_ eq __PACKAGE__ ? $_->Resolve($TypeTable,$DoNotCreate) : Schema::TypeName->new($_)->Resolve($TypeTable,$DoNotCreate)} @{$this->{TemplateList}} );
+        } else {
+            die new Exception("Simple type not found", $this->Name);
+        }
+    }
+}
+
+package Schema::TypeTable;
+use Common;
+our @ISA = qw(Object);
+
+BEGIN {
+    DeclareProperty(Table => ACCESS_NONE);
+    DeclareProperty(NextTable => ACCESS_NONE);
+}
+
+sub CTOR {
+    my ($this,$nextTable) = @_;
+    $this->{$NextTable} = $nextTable;
+}
+
+sub ResolveType {
+    my ($this,$TypeName,@args) = @_;
+    
+    if (my $Type = $this->{$Table}->{$TypeName->CanonicalName}) {
+        return $Type;
+    } elsif($this->{$NextTable}) {
+        return $this->{$NextTable}->ResolveType($TypeName,@args);
+    } else {
+        return undef;
+    }
+}
+
+sub RegisterType {
+    my ($this,$Type) = @_;
+    
+    if (not $this->{$Table}->{$Type->Name->CanonicalName}) {
+        $this->{$Table}->{$Type->Name->CanonicalName} = $Type;
+    } else {
+        die new Exception("A type already registered",$Type->Name->CanonicalName);
+    }
+}
+
+sub _ListTypes {
+    my $this = shift;
+    return values %{$this->{$Table}};
+}
+
+sub Dispose {
+    my $this = shift;
+    
+    $_->Dispose foreach values %{$this->{$Table} ? $this->{$Table} : {} };
+    
+    delete $this->{$Table};
+    
+    $this->SUPER::Dispose;
+}
+
+# Ñïåöèàëèçàöèÿ øàáëîíà - ýòî èìÿ ñïåöèàëèçèðóåìîãî øàáëîíà è ïàðàìåòðû, êîòîðûå áóäóò åìó ïåðåäàíû (âàæåí ïîðÿäîê ïàðàìåòðîâ)
+# Ñïåöèàëèçàöèÿ øàáëîíà ïàðàìåòðàìè ïîðàæäàåò ÷àñòè÷íî ñïåöèàëèçèðîâàííûé øàáëîí, êîòîðûé ïî ñóòè òàêæå ÿâëÿåòñÿ øàáëîíîì
+# Åñëè ñïåöèàëèçàöèÿ ïîëíàÿ, òî ìîæíî ñîçäàòü ýêçåìïëÿð øàáëîíà, òîåñòü ïîëíîöåííûé òèï
+package Schema::TemplateSpec;
+use Common;
+our @ISA = qw(Object);
+
+BEGIN {
+    DeclareProperty(Name => ACCESS_READ);
+    DeclareProperty(Parameters => ACCESS_READ);
+    DeclareProperty(TemplateList => ACCESS_READ);
+}
+
+sub CTOR {
+    my ($this,$templateName,@typeList) = @_;
+
+    my %Params;
+
+    $this->{$TemplateList} = \@typeList;
+
+    # âû÷èñëÿåì ïàðàìåòðû äàííîé ñïåöèàëèçàöèè
+    my @nameList;
+    foreach $typeItem (@typeList) {
+        map { $Params{$_->Name} = $_ } @{$typeItem->Parameters} if $typeItem->isTemplate;
+        push @nameList, $typeItem->Name;
+    }
+
+    $this->{$Parameters} = [ values %Params ];
+    $this->{$Name} = new Schema::TypeName($templateName,@nameList);
+}
+
+sub isTemplate {
+    1;
+}
+
+sub canInstantinate {
+    my ($this) = @_;
+    if (@{$this->{$Parameters}}) {
+        0;
+    } else {
+        1;
+    }
+}
+
+sub Specialize {
+    my ($this,$refParams,$TypeTable) = @_;
+    
+    my @specializedList = map {$_->isTemplate && !$_->canInstantinate ? $_->Specialize($refParams,$TypeTable) : $_ } @{$this->{$TemplateList}};
+
+    if ($TypeTable) {
+        
+        my $TypeName = new Schema::TypeName($this->Name->Name,map {$_->Name} @specializedList);
+        my $templateSpec = $TypeTable->ResolveType($TypeName);
+        if (not $templateSpec) {
+            $templateSpec = new Schema::TemplateSpec($this->Name->Name,@specializedList);
+            $TypeTable->RegisterType($templateSpec);
+        }
+        return $templateSpec;
+    } else {
+        return new Schema::TemplateSpec($this->Name->Name,@specializedList);
+    }
+}
+
+# Ïàðàìåòð øàáëîíà
+# Ïî ñóòè ÿâëÿåòñÿ øàáëîíîì òèïà Param_Name<T> -> T;
+package Schema::Parameter;
+
+sub new {
+    my $TypeName = new Schema::TypeName($_[1]);
+    bless \$TypeName,$_[0];
+}
+
+sub Name {
+    ${shift()};
+}
+
+sub Specialize {
+    my ($this,$refArgs) = @_;
+    return $refArgs->{$$this->Name};
+}
+
+sub isTemplate {
+    1;
+}
+
+sub canInstantinate {
+    0;
+}
+
+sub Parameters {
+    if (wantarray) {
+        shift;
+    } else {
+        [shift];
+    }
+}
+
+
+# ×ëåí êëàññà
+package Schema::Member;
+use Common;
+our @ISA = qw(Object);
+our $Abstract = 1;
+
+BEGIN {
+    DeclareProperty(Name => ACCESS_READ);
+}
+sub CTOR {
+    my($this,$name) =  @_;
+    
+    $this->{$Name} = $name;
+}
+
+# ×ëåí êëàññà - ñâîéñòâî.
+# Ñâîéñòâî ìîæåò áûòü øàáëîíîì, åñëè øàáëîíîì ÿâëÿåòñÿ åãî òèï
+package Schema::Property;
+use Common;
+our @ISA = qw(Schema::Member);
+
+BEGIN {
+    DeclareProperty(Type => ACCESS_READ);
+}
+
+sub CTOR {
+    my ($this,$name,$type) = @_;
+    $this->SUPER::CTOR($name);
+    
+    $this->{$Type} = $type or die new Exception("A type for the property must be specified",$name);
+}
+
+sub isTemplate {
+    my $this = shift;
+    return $this->{$Type}->isTemplate;
+}
+
+sub canInstantinate {
+    my $this = shift;
+    return $this->{$Type}->canInstantinate;
+}
+
+sub Instantinate {
+    my ($this,$Schema) = @_;
+    return new Schema::Property($this->Name,$Schema->Instantinate($this->{$Type}));
+}
+
+sub Specialize {
+    my ($this,$refParams,$TypeTable) = @_;
+    return new Schema::Property($this->Name,$this->{$Type}->Specialize($refParams,$TypeTable));
+}
+
+# Òèï, îïèñûâàåò òèï îáúåêòà
+package Schema::Type;
+use Common;
+our @ISA = qw(Object);
+
+BEGIN {
+    DeclareProperty(Name => ACCESS_READ);
+    DeclareProperty(Schema => ACCESS_READ);
+    DeclareProperty(Members => ACCESS_READ);
+    DeclareProperty(BaseList => ACCESS_READ);
+    DeclareProperty(Attributes => ACCESS_READ); #hash of attributes
+}
+
+sub CTOR {
+    my ($this,$argSchema,$name) = @_;
+    
+    $this->{$Name} = ref $name eq 'Schema::TypeName' ? $name : new Schema::TypeName($name);
+    $this->{$Schema} = $argSchema;
+}
+
+sub isTemplate {
+    0;
+}
+
+sub Equals {
+    my ($this,$other) = @_;
+    if (UNIVERSAL::isa($other,'Schema::Type')) {
+        return ($this->Name->CanonicalName eq $other->Name->CanonicalName);
+    } else {
+        return 1;
+    }
+}
+
+sub CreateProperty {
+    my ($this,$PropName,$TypeName) = @_;
+
+    $PropType = $this->_ResolveType($TypeName);
+
+    return new Schema::Property($PropName,$PropType);
+}
+
+sub AddBase {
+    my ($this,$type) = @_;
+    
+    $type = $this->_ResolveType($type);
+    
+    not $type->isType($this) or die new Exception('Cant derive from the class which is derived from self', $this->Name->CanonicalName, $type->Name->CanonicalName);
+    
+    push @{$this->{$BaseList}},$type;
+}
+
+sub isType {
+    my ($this,$type,$maxlevel) = @_;
+    
+    return 0 if defined $maxlevel and $maxlevel < 0;
+    my $typeName = UNIVERSAL::isa($type,'Schema::Type') ? $type->Name : $type ;
+    
+    return (
+        $this->{$Name}->CanonicalName eq $typeName->CanonicalName ?
+            1
+        :
+            scalar (grep {$_->isType($typeName,defined $maxlevel ? $maxlevel - 1 : undef)} $this->BaseList)
+    );
+}
+
+sub ValidateType {
+    my ($this,$type) = @_;
+    
+    die new Exception('Can\'t use an unspecialized template',$type->Name->CanonicalName) if ($type->isa('Schema::TypeTemplate'));
+        
+    if ($type->isTemplate and not $type->canInstantinate) {
+        die new Exception('Cant use a not fully specialized template in a simple type',$type->Name->CanonicalName, $this->Name->Name) if not $this->isTemplate;
+        
+        my %Params = map {$_->Name->Name() , 1} @{$this->Parameters};
+        my @Unresolved = grep {not $Params{$_->Name->Name}} @{$type->Parameters()};
+        
+        die new Exception('Not all parameters can be rsolved',map {$_->Name->Name} @Unresolved) if @Unresolved;
+    }
+}
+
+sub InsertProperty {
+    my ($this,$PropName,$PropType) = @_;
+    
+    $PropType = $this->_ResolveType($PropType);
+
+    my $prop = new Schema::Property($PropName,$PropType);
+    
+    push @{$this->{$Members}}, $prop;
+
+    return $prop;
+}
+
+sub AddMember {
+    my ($this,$member) = @_;
+    
+    push @{$this->{$Members}},$member;
+}
+
+sub GetTypeTable {
+    my $this = shift;
+    return $this->{$Schema};
+}
+
+sub _ResolveType {
+    my ($this,$type) = @_;
+    if ($type->isa('Schema::TypeName')) {
+        $type = $type->Resolve($this->GetTypeTable());
+    } elsif ($type->isa('Schema::Type') or $type->isa('Schema::TemplateSpec')) {
+        $this->ValidateType($type);
+    } else {
+        die new Exception('Invalid type',$type);
+    }
+    
+    $type = $this->{$Schema}->Instantinate($type) if ($type->isTemplate and $type->canInstantinate and not $this->isTemplate);
+    return $type;
+}
+
+sub ListMembers {
+    my ($this,%options) = @_;
+    
+    my @members;
+    
+    if ($options{'foreign'}) {
+        push @members, $_->isa('Schema::Type') ? $_->ListMembers(%options) : () foreach @{$this->{$BaseList} ? $this->{$BaseList} : []};
+    }
+    push @members, @{$this->{$Members} ? $this->{$Members} : []};
+    
+    return @members;
+}
+
+sub FindMembers {
+    my ($this,$memberName,%options) = @_;
+    
+    my @members = grep { $_->Name eq $memberName} @{$this->{$Members} ? $this->{$Members} : []};
+    
+    if ($options{'deep'}) {
+        push @members,$_->ListMembers(%options) foreach @{$this->{$BaseList} ? $this->{$BaseList} : []};
+    }
+    
+    if(wantarray) {
+        return @members;
+    } else {
+        return shift @members;
+    }
+}
+
+sub SetAttributes {
+    my ($this,%attributes) = @_;
+    
+    while (my ($key,$value) = each %attributes) {
+        $this->{$Attributes}{$key} = $value;
+    }
+}
+
+sub GetAttribute {
+    my ($this,$name) = @_;
+    
+    return $this->{$Attributes}{$name};
+}
+
+sub _dump {
+    my ($this) = @_;
+    return $this->Name->CanonicalName;
+}
+
+sub Dispose {
+    my ($this) = @_;
+    
+    undef %{$this};
+    $this->SUPER::Dispose;
+}
+
+# Øàáëîí - ïðàìåòðèçîâàííûé òèï
+package Schema::Template;
+use Common;
+our @ISA = qw(Schema::Type);
+
+BEGIN {
+    DeclareProperty(Parameters => ACCESS_READ);
+    DeclareProperty(LocalTypes => ACCESS_NONE);
+
+}
+
+sub CTOR {
+    my ($this,$Schema,$name,@args) = @_;
+    # ïàðàìåòðû íå ÿâëÿþòñÿ ÷à÷òüþ èìåíè
+    $this->SUPER::CTOR($Schema,$name);
+    
+    $this->{$Parameters} = [ map {new Schema::Parameter($_) } @args ];
+    my $TypeTable = new Schema::TypeTable($Schema);
+    $TypeTable->RegisterType($_) foreach @{$this->{$Parameters} };
+    $this->{$LocalTypes} = $TypeTable;
+}
+
+sub GetTypeTable {
+    my ($this) = @_;
+    return $this->{$LocalTypes};
+}
+
+sub isTemplate {
+    1;
+}
+
+sub Specialize {
+    my ($this,$refArgs,$TypeTable) = @_;
+    
+    my @specializedList = map {$_->Specialize($refArgs)} @{$this->{$Parameters}};
+    
+    # ñîçäàåì ñïåöèàëèçàöèþ øàáëîíà
+    my $specializedType;
+    
+    if ($TypeTable) {
+        my $TypeName = new Schema::TypeName($this->Name->Name,map {$_->Name} @specializedList);
+        
+        if(my $specializedType = $TypeTable->ResolveType($TypeName)) {
+            return $specializedType;
+        } else {
+            $specializedType = new Schema::TemplateSpec($this->Name->Name, @specializedList );
+            $TypeTable->RegisterType($specializedType);
+            return $specializedType;
+        }
+    } else {
+        return new Schema::TemplateSpec($this->Name->Name, @specializedList );
+    }
+}
+
+sub canInstantinate {
+    0;
+}
+
+# ñîçäàíèå ýêçåìïëÿðà øàáëîíà.
+# Ñîçäàòü øàáëîí = ïîëíîñòüþ åãî ñïåöèàëèçèðîâàòü
+# Ïðèíèìàåò íàáîð ïàðàìåòðîâ øàáëîíà è ñîçäàåò íîâûé òèï èëè âîçâðàùàåò èç ñõåìû
+sub Instantinate {
+    my ($this,$refArgs,$instance) = @_;
+
+    my %ParamInstances;
+    my @TemplateListNames;
+    
+    foreach my $param (@{$this->{$Parameters}}) {
+        my $type = $refArgs->{$param->Name->Name};
+        die new Exception("Parameter not specified",$param->Name->Name) if not $type;
+        if ($type->isTemplate) {
+            if ($type->canInstantinate) {
+                $type = $this->Schema->Instantinate($type);
+            } else {
+                die new Exception("Parameter must be a fully speciazlied type",$param->Name->Name);
+            }
+        }
+        
+        $ParamInstances{$param->Name->Name} = $type;
+        push @TemplateListNames, $type->Name;
+    }
+    
+    # ïàðàìåòðû ïðåäñòàâëÿþò ñîáîé ðåàëüíûå òèïû, ïåðåõîäèì ê ñîçäàíèþ òèïà
+    # äàííàÿ ôóíêöèÿ áåóñëîâíî ñîçäàåò íîâûé òèï, ýòó ôóíêöèþ èñïîëüçóåò ñõåì
+    
+    $instance = $this->Schema->CreateType( new Schema::TypeName($this->Name->Name,@TemplateListNames) ) if not $instance;
+
+    $instance->SetAttributes(%{$this->Attributes}) if $this->Attributes;
+    $instance->SetAttributes(
+        TemplateInstance => {
+            Template => $this,
+            Parameters => \%ParamInstances
+        }
+    );
+    
+    foreach my $Ancestor ($this->BaseList) {
+        $instance->AddBase(
+            $Ancestor->isTemplate ?
+                ( $Ancestor->canInstantinate ?
+                    $this->Schema->Instantinate($Ancestor)
+                    :
+                    $this->Schema->Instantinate($Ancestor->Specialize(\%ParamInstances,$this->GetTypeTable))
+                )
+                :
+                $Ancestor
+        );
+    }
+    
+    foreach my $Member ($this->Members) {
+        $instance->AddMember(
+            $Member->isTemplate ?
+                ($Member->canInstantinate ?
+                    $Member->Instantinate($this->Schema)
+                    :
+                    $Member->Specialize(\%ParamInstances,$this->GetTypeTable)->Instantinate($this->Schema)
+                )
+                :
+                $Member
+        );
+    }
+
+    return $instance;
+}
+
+sub _ResolveType {
+    my ($this,$type) = @_;
+    if ($type->isa('Schema::TypeName')) {
+        $type = $type->Resolve($this->GetTypeTable());
+        if (not $this->{$LocalTypes}->ResolveType($type->Name)) {
+            $this->{$LocalTypes}->RegisterType($type);
+        }
+    } elsif ($type->isa('Schema::Type') or $type->isa('Schema::TemplateSpec')) {
+        $this->ValidateType($type);
+    } else {
+        die new Exception('Invalid type',$type);
+    }
+
+    return $type;
+}
+
+
+package Schema;
+use strict;
+use Common;
+our @ISA = qw(Schema::TypeTable);
+
+BEGIN {
+    DeclareProperty(PendingInstances => ACCESS_NONE);
+    DeclareProperty(UnresolvedTypes => ACCESS_NONE);
+}
+
+sub CTOR {
+    
+}
+
+# Ñõåìà àâòîìàòè÷åñêè ñîçäàåò ññûëêè âïåðåä íà íåñóùåñòâóþùèå ïðîñòûå òèïû
+sub ResolveType {
+    my ($this,$TypeName,$DoNotCreate) = @_;
+    
+    if (my $type = $this->SUPER::ResolveType($TypeName)) {
+        return $type;
+    } else {
+        if (not $TypeName->isTemplateSpec and not $DoNotCreate) {
+            $type = new Schema::Type($this,$TypeName);
+            $this->RegisterType($type);
+            $this->{$UnresolvedTypes}->{$TypeName->CanonicalName} = $TypeName;
+            return $type;
+        } else {
+            return undef;
+        }
+    }
+}
+
+sub CreateType {
+    my ($this,$TypeName) = @_;
+    
+    $TypeName = new Schema::TypeName($TypeName) if ref $TypeName ne 'Schema::TypeName';
+    
+    if (my $type = $this->SUPER::ResolveType($TypeName)) {
+        if ($this->{$UnresolvedTypes}->{$TypeName->CanonicalName}) {
+            delete $this->{$UnresolvedTypes}->{$TypeName->CanonicalName};
+            return $type;
+        } else {
+            die new Exception("Type already exists",$TypeName->CanonicalName);
+        }
+    } else {
+        $type = new Schema::Type($this,$TypeName);
+        $this->SUPER::RegisterType($type);
+        return $type;
+    }
+}
+
+sub CreateTemplate {
+    my ($this,$TemplateName,@ParamNames) = @_;
+    
+    die new Exception("Parameters required for the template") if not @ParamNames;
+    
+    if (ref $TemplateName eq 'Schema::TypeName') {
+        die new Exception('Template specialization is not valid name for a new template',$TemplateName->CanonicalName) if $TemplateName->isTemplateSpec;
+    } else {
+        $TemplateName = new Schema::TypeName($TemplateName);
+    }
+    
+    if (my $type = $this->SUPER::ResolveType($TemplateName)) {
+        die new Exception('Type already exists');
+    } else {
+        $type = new Schema::Template($this,$TemplateName,@ParamNames);
+        $this->SUPER::RegisterType($type);
+        return $type;
+    }
+}
+
+# ñîçäàíèå ýêçåìïëÿðà øàáëîíà
+# ñîçäàåòñÿ íîâûé ïóñòîé òèï, äîáàâëÿåòñÿ â PendingInstances
+sub Instantinate {
+    my ($this,$TemplateSpec) = @_;
+    
+    # ïðè ñïåöèàëèçàöèè íàïðìåð ýòîãî: T m_var; ïîëó÷èì äëÿ èíñòàíòèíèöèè real_type m_var; è íå ïðîâåðÿÿ îòäàäèì åãî íà ñïåöèàëèçàöèþ,
+    # âîò è îáðàáîòêà
+    return $TemplateSpec if not $TemplateSpec->isTemplate;
+    
+    die new Exception('Only a template specialization can be instantinated') if ref $TemplateSpec ne 'Schema::TemplateSpec';
+    die new Exception('Only fully specialized template can be instantinated') if not $TemplateSpec->canInstantinate;
+    
+    my $TypeName = $TemplateSpec->Name;
+    
+    if (my $type = $this->SUPER::ResolveType($TypeName)) {
+        return $type;
+    } else {
+        $type = new Schema::Type($this,$TypeName);
+        $this->SUPER::RegisterType($type);
+        push @{$this->{$PendingInstances}},[$TemplateSpec,$type];
+        return $type;
+    }
+}
+
+sub Close {
+    my ($this) = @_;
+    
+    if (keys %{$this->{$UnresolvedTypes}}) {
+        die new Exception('Some type definitions are absent',keys %{$this->{$UnresolvedTypes}});
+    }
+    
+    if ($this->{$PendingInstances}) {
+        while( my $ref = shift @{$this->{$PendingInstances}} ) {
+            my ($spec,$instance) = @$ref;
+            if (my $typeTemplate = $this->SUPER::ResolveType( new Schema::TypeName($spec->Name->Name) )) {
+                die new Exception('Can\'t instantinate a specialization of the simple type',$instance->Name->CanonicalName) if not $typeTemplate->isTemplate;
+                if (scalar(@{$typeTemplate->Parameters}) == scalar(@{$spec->TemplateList})) {
+                    my @Params = @{$typeTemplate->Parameters};
+                    $typeTemplate->Instantinate({map { (shift @Params)->Name->Name, $_ } @{$spec->TemplateList}},$instance);
+                } else {
+                    die new Exception('A template parameters doesn\'t match to the specialization list',$instance->Name->CanonicalName);
+                }
+            } else {
+                die new Exception('Can\'t instantinate a specialization, the specified template isn\'t found', $instance->Name->CanonicalName);
+            }
+        }
+        
+        delete $this->{$PendingInstances};
+    }
+}
+
+sub EnumTypes {
+    my ($this,%options) = @_;
+    
+    return grep { ($_->isTemplate and not $options{'skip_templates'}) or (not $_->isTemplate and not $options{'skip_classes'}) } $this->_ListTypes;
+}
+
+sub Dispose {
+    my ($this) = @_;
+    
+    delete $this->{$UnresolvedTypes};
+    
+    $this->SUPER::Dispose;
+}
+
+1;