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