Mercurial > pub > Impl
diff lib/IMPL/Web/View/Metadata/FormMeta.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/IMPL/Web/View/Metadata/FormMeta.pm Fri Sep 04 19:40:23 2015 +0300 @@ -0,0 +1,319 @@ +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 \ No newline at end of file