comparison 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
comparison
equal deleted inserted replaced
406:f23fcb19d3c1 407:c6e90e02dd17
1 package IMPL::Web::View::Metadata::FormMeta;
2 use strict;
3
4 use IMPL::lang;
5 use IMPL::Const qw(:prop);
6 use IMPL::declare {
7 require => {
8 Exception => 'IMPL::Exception',
9 ArgException => '-IMPL::InvalidArgumentException',
10 OpException => '-IMPL::InvalidOperationException',
11 SchemaNavigator => 'IMPL::DOM::Navigator::SchemaNavigator',
12 DOMNode => '-IMPL::DOM::Node'
13 },
14 base => [
15 'IMPL::Web::View::Metadata::BaseMeta' => '@_'
16 ],
17 props => [
18 nodes => PROP_RO,
19 decl => PROP_RO,
20 schema => PROP_RO,
21 errors => PROP_RO,
22 group => PROP_RO
23 ]
24 };
25
26 use constant {
27 Meta => __PACKAGE__
28 };
29
30 sub CTOR {
31 my ($this,$model,$type,$args) = @_;
32
33 if ($args) {
34 $this->$_($args->{$_}) foreach grep $args->{$_}, qw(decl schema nodes errors group);
35 }
36
37 $this->$_() || die ArgException->new($_ => "The $_ is required")
38 foreach qw(schema);
39 }
40
41 sub GetSchemaProperty {
42 my ($this,$name) = @_;
43
44 return $this->decl ? $this->decl->nodeProperty($name) || $this->schema->nodeProperty($name) : $this->schema->nodeProperty($name);
45 }
46
47 sub template {
48 shift->GetSchemaProperty('template');
49 }
50
51 sub label {
52 shift->GetSchemaProperty('label');
53 }
54
55 sub inputType {
56 shift->GetSchemaProperty('inputType');
57 }
58
59 sub inputValue {
60 my ($this) = @_;
61
62 if($this->isMultiple) {
63 return [
64 map {
65 $_ ? $_->nodeValue || $_->nodeProperty('rawValue') : undef
66 }
67 @{$this->model || []}
68 ]
69 } else {
70 return $this->model ? $this->model->nodeValue || $this->model->nodeProperty('rawValue') : undef;
71 }
72 }
73
74 sub isMultiple {
75 my ($this) = @_;
76 $this->decl && $this->decl->isMultiple;
77 }
78
79 sub isOptional {
80 my ($this) = @_;
81 not($this->decl) || $this->decl->isOptional;
82 }
83
84 sub GetOwnErrors {
85 my ($this) = @_;
86
87 my $nodes = $this->nodes;
88
89 my $errors = [
90 grep _IsOwnError($nodes,$this->decl,$_), @{$this->errors || []}
91 ];
92
93 return $errors;
94 }
95
96 sub _IsOwnError {
97 my ($nodes,$source,$err) = @_;
98
99 return 1 if ($err->node && grep($err->node == $_, @$nodes)) || (not(@$nodes) && $err->schemaNode && $err->schemaNode == $source );
100
101 return 0;
102 }
103
104 sub _IsErrorRelates {
105 my ($nodes,$source,$err) = @_;
106
107 # this is an own error
108 return 1 if _IsOwnError($nodes,$source,$err);
109
110 # this error relates to the child control
111
112 return 0 unless @$nodes;
113
114 for (my $n = $err->parent; $n ; $n = $n->parentNode) {
115 return 1 if grep($n == $_, @$nodes);
116 }
117
118 return 0;
119 }
120
121 sub PopulateProperties {
122 my ($this) = @_;
123
124 my @props;
125
126 # return empty list of properties in case of multiple values
127 return \@props if $this->isMultiple;
128
129 my $navi = SchemaNavigator->new($this->schema);
130
131 foreach my $decl (@{$this->schema->content->childNodes}) {
132
133 my $schema = $navi->NavigateName($decl->name);
134 $navi->SchemaBack();
135
136 my @nodes = $this->model && $this->model->selectNodes( sub { $_->schemaNode == $decl } );
137
138 my %args = (
139 name => $decl->name,
140 decl => $decl,
141 schema => $schema,
142 nodes => [@nodes],
143 errors => [grep _IsErrorRelates(\@nodes,$decl,$_), @{$this->errors || []}]
144 );
145
146 my ($model,$type);
147
148 if ($decl->isMultiple) {
149 $model = [@nodes];
150 $type = 'ARRAY';
151 $args{holdingType} = $schema->type;
152 } else {
153 $model = shift @nodes;
154 $type = $schema->type;
155 }
156
157 push @props, Meta->new($model,$type,\%args);
158 }
159
160 return \@props;
161 }
162
163 sub GetItems {
164 my ($this) = @_;
165
166 die OpException->new("The operation must be performed on the container")
167 unless $this->isMultiple;
168
169 my $i = 0;
170
171 return [
172 map $this->_GetItemMeta($_,$i++), @{$this->nodes}
173 ];
174 }
175
176 sub GetItem {
177 my ($this,$index) = @_;
178
179 die OpException->new("The operation must be performed on the container")
180 unless $this->isMultiple;
181
182 my $node = $this->nodes->[$index];
183
184 return $this->_GetItemMeta($node,$index);
185 }
186
187 sub _GetItemMeta {
188 my ($this,$node,$index) = @_;
189
190 my @nodes;
191 push @nodes,$node if $node;
192
193 return Meta->new(
194 $node,
195 $this->schema->type,
196 {
197 name => $index,
198 schema => $this->schema,
199 errors => [grep _IsErrorRelates([$node],$this->decl,$_), @{$this->errors ||[]} ],
200 group => $this,
201 nodes => \@nodes
202 }
203 );
204 }
205
206 sub GetMetadataForModel {
207 my ($self,$model,$args) = @_;
208
209 $args ||= {};
210
211 my $modelType = delete $args->{modelType};
212
213 if($model) {
214 die ArgException->new(model => "A node is required")
215 unless is($model,DOMNode);
216
217 $args->{decl} ||= $model->schemaNode;
218 $args->{schema} ||= $model->schemaType;
219 }
220
221 return $self->new(
222 $model,
223 $modelType,
224 $args
225 );
226 }
227
228 1;
229
230 __END__
231
232 =pod
233
234 =head1 NAME
235
236 =head1 SYNOPSIS
237
238 =head1 DESCRIPTION
239
240 Расширенные метаданные модели для элементов формы, помимо стандартных свойств
241 сожержит в себе информацию о схеме.
242
243 =head1 MEMBERS
244
245 =head2 C<[get]errors>
246
247 Ссылка на массив с ошибками при проверке схемы. Ошибки относятся ко всем
248 узлам в текущей модели, включая вложенные и т.п.
249
250 =head2 C<[get]model>
251
252 Ссылка на элемент документа, либо на массив с элементами для множественных
253 значений (C<isMultiple = true>). В том случае, когда документ был не
254 корректен и для не множественного элемента было передено несколько значений,
255 данное свойство будет содержать только первое.
256
257 =head2 C<[get]nodes>
258
259 Ссылка на массив с узлами документа. В теории количество узлов может быть
260 произвольным, поскольку документ может быть некорректным, т.е. их может
261 быть более одного в то время, как C<isMultiple = false> или, напротив, ни
262 одного при C<isOptional = false>.
263
264 Как правило для построения формы данное свойство не требуется.
265
266 =head2 C<[get]modelType>
267
268 Название типа данных из схемы документа (C<< schema->name >>), если тип не имеет название, то это
269 C<ComplexNode> для сложных узлов и C<SimpleNode> для простых.
270
271 Для моделей с множественными значениями это свойство не задано. Тип элементов
272 храниться в свойстве C<holdingType>
273
274 =head2 C<[get]decl>
275
276 Объявление элемента формы, объявление может совпадать со схемой в случае,
277 когда это был C<SimpleNode> или C<ComplexNode>, иначе это C<Node> ссылающийся
278 на заранее обпределенный тип.
279
280 =head2 C<[get]schema>
281
282 Схема текущего элемента, C<СomlexType>, C<SimpleType>, C<ComplexNode> или
283 C<SimpleNode>.
284
285 =head2 C<[get]isOptional>
286
287 Данный элемент может не иметь ни одного значения
288
289 =head2 C<[get]isMultiple>
290
291 Данный элемент может иметь более одного значения. Модель с множественными
292 значениями является сложным элементом, в котором дочерними моделями являются
293 не свойства а сами элементы, в данном случае они их именами будут индексы.
294
295 =begin code
296
297 for(my $i=0; $i< 10; $i++) {
298 display_for($i,'template');
299 }
300
301 sub display_for {
302 my ($index,$tmpl) = @_;
303
304 if ($index =~ /^\d+$/) {
305 return render($tmpl, metadata => { $meta->GetItem($index) });
306 } else {
307 return render($tmpl, metadata => { $meta->GetProperty($index) });
308 }
309 }
310
311 =end code
312
313 =head2 C<GetOwnErrors()>
314
315 Возвращает ошибки относящиеся к самому элементу C<model>, это принципиально
316 для контейнеров и в случаях, когда модель не корректна и в ней присутствуют
317 лишние значения.
318
319 =cut