Mercurial > pub > Impl
view Lib/IMPL/Web/View/Metadata/FormMeta.pm @ 389:5aff94ba842f
DOM Schema refactoring complete
author | cin |
---|---|
date | Wed, 12 Feb 2014 13:36:24 +0400 |
parents | 4cc6cc370fb2 |
children |
line wrap: on
line source
package IMPL::Web::View::Metadata::FormMeta; use strict; use IMPL::lang; use IMPL::Const qw(:prop); use IMPL::declare { require => { Exception => 'IMPL::Exception', ArgException => '-IMPL::InvalidArgumentException', OpException => '-IMPL::InvalidOperationException', SchemaNavigator => 'IMPL::DOM::Navigator::SchemaNavigator', DOMNode => '-IMPL::DOM::Node' }, base => [ 'IMPL::Web::View::Metadata::BaseMeta' => '@_' ], props => [ nodes => PROP_RO, decl => PROP_RO, schema => PROP_RO, errors => PROP_RO, group => PROP_RO ] }; use constant { Meta => __PACKAGE__ }; sub CTOR { my ($this,$model,$type,$args) = @_; if ($args) { $this->$_($args->{$_}) foreach grep $args->{$_}, qw(decl schema nodes errors group); } $this->$_() || die ArgException->new($_ => "The $_ is required") foreach qw(schema); } sub GetSchemaProperty { my ($this,$name) = @_; return $this->decl ? $this->decl->nodeProperty($name) || $this->schema->nodeProperty($name) : $this->schema->nodeProperty($name); } sub template { shift->GetSchemaProperty('template'); } sub label { shift->GetSchemaProperty('label'); } sub inputType { shift->GetSchemaProperty('inputType'); } sub inputValue { my ($this) = @_; if($this->isMultiple) { return [ map { $_ ? $_->nodeValue || $_->nodeProperty('rawValue') : undef } @{$this->model || []} ] } else { return $this->model ? $this->model->nodeValue || $this->model->nodeProperty('rawValue') : undef; } } sub isMultiple { my ($this) = @_; $this->decl && $this->decl->isMultiple; } sub isOptional { my ($this) = @_; not($this->decl) || $this->decl->isOptional; } sub GetOwnErrors { my ($this) = @_; my $nodes = $this->nodes; my $errors = [ grep _IsOwnError($nodes,$this->decl,$_), @{$this->errors || []} ]; return $errors; } sub _IsOwnError { my ($nodes,$source,$err) = @_; return 1 if ($err->node && grep($err->node == $_, @$nodes)) || (not(@$nodes) && $err->schemaNode && $err->schemaNode == $source ); return 0; } sub _IsErrorRelates { my ($nodes,$source,$err) = @_; # this is an own error return 1 if _IsOwnError($nodes,$source,$err); # this error relates to the child control return 0 unless @$nodes; for (my $n = $err->parent; $n ; $n = $n->parentNode) { return 1 if grep($n == $_, @$nodes); } return 0; } sub PopulateProperties { my ($this) = @_; my @props; # return empty list of properties in case of multiple values return \@props if $this->isMultiple; my $navi = SchemaNavigator->new($this->schema); foreach my $decl (@{$this->schema->content->childNodes}) { my $schema = $navi->NavigateName($decl->name); $navi->SchemaBack(); my @nodes = $this->model && $this->model->selectNodes( sub { $_->schemaNode == $decl } ); my %args = ( name => $decl->name, decl => $decl, schema => $schema, nodes => [@nodes], errors => [grep _IsErrorRelates(\@nodes,$decl,$_), @{$this->errors || []}] ); my ($model,$type); if ($decl->isMultiple) { $model = [@nodes]; $type = 'ARRAY'; $args{holdingType} = $schema->type; } else { $model = shift @nodes; $type = $schema->type; } push @props, Meta->new($model,$type,\%args); } return \@props; } sub GetItems { my ($this) = @_; die OpException->new("The operation must be performed on the container") unless $this->isMultiple; my $i = 0; return [ map $this->_GetItemMeta($_,$i++), @{$this->nodes} ]; } sub GetItem { my ($this,$index) = @_; die OpException->new("The operation must be performed on the container") unless $this->isMultiple; my $node = $this->nodes->[$index]; return $this->_GetItemMeta($node,$index); } sub _GetItemMeta { my ($this,$node,$index) = @_; my @nodes; push @nodes,$node if $node; return Meta->new( $node, $this->schema->type, { name => $index, schema => $this->schema, errors => [grep _IsErrorRelates([$node],$this->decl,$_), @{$this->errors ||[]} ], group => $this, nodes => \@nodes } ); } sub GetMetadataForModel { my ($self,$model,$args) = @_; $args ||= {}; my $modelType = delete $args->{modelType}; if($model) { die ArgException->new(model => "A node is required") unless is($model,DOMNode); $args->{decl} ||= $model->schemaNode; $args->{schema} ||= $model->schemaType; } return $self->new( $model, $modelType, $args ); } 1; __END__ =pod =head1 NAME =head1 SYNOPSIS =head1 DESCRIPTION Расширенные метаданные модели для элементов формы, помимо стандартных свойств сожержит в себе информацию о схеме. =head1 MEMBERS =head2 C<[get]errors> Ссылка на массив с ошибками при проверке схемы. Ошибки относятся ко всем узлам в текущей модели, включая вложенные и т.п. =head2 C<[get]model> Ссылка на элемент документа, либо на массив с элементами для множественных значений (C<isMultiple = true>). В том случае, когда документ был не корректен и для не множественного элемента было передено несколько значений, данное свойство будет содержать только первое. =head2 C<[get]nodes> Ссылка на массив с узлами документа. В теории количество узлов может быть произвольным, поскольку документ может быть некорректным, т.е. их может быть более одного в то время, как C<isMultiple = false> или, напротив, ни одного при C<isOptional = false>. Как правило для построения формы данное свойство не требуется. =head2 C<[get]modelType> Название типа данных из схемы документа (C<< schema->name >>), если тип не имеет название, то это C<ComplexNode> для сложных узлов и C<SimpleNode> для простых. Для моделей с множественными значениями это свойство не задано. Тип элементов храниться в свойстве C<holdingType> =head2 C<[get]decl> Объявление элемента формы, объявление может совпадать со схемой в случае, когда это был C<SimpleNode> или C<ComplexNode>, иначе это C<Node> ссылающийся на заранее обпределенный тип. =head2 C<[get]schema> Схема текущего элемента, C<СomlexType>, C<SimpleType>, C<ComplexNode> или C<SimpleNode>. =head2 C<[get]isOptional> Данный элемент может не иметь ни одного значения =head2 C<[get]isMultiple> Данный элемент может иметь более одного значения. Модель с множественными значениями является сложным элементом, в котором дочерними моделями являются не свойства а сами элементы, в данном случае они их именами будут индексы. =begin code for(my $i=0; $i< 10; $i++) { display_for($i,'template'); } sub display_for { my ($index,$tmpl) = @_; if ($index =~ /^\d+$/) { return render($tmpl, metadata => { $meta->GetItem($index) }); } else { return render($tmpl, metadata => { $meta->GetProperty($index) }); } } =end code =head2 C<GetOwnErrors()> Возвращает ошибки относящиеся к самому элементу C<model>, это принципиально для контейнеров и в случаях, когда модель не корректна и в ней присутствуют лишние значения. =cut