Mercurial > pub > Impl
diff Lib/Schema.pm @ 49:16ada169ca75
migrating to the Eclipse IDE
author | wizard@linux-odin.local |
---|---|
date | Fri, 26 Feb 2010 10:49:21 +0300 |
parents | 03e58a454b20 |
children |
line wrap: on
line diff
--- a/Lib/Schema.pm Fri Feb 26 01:43:42 2010 +0300 +++ b/Lib/Schema.pm Fri Feb 26 10:49:21 2010 +0300 @@ -1,748 +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; +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;