Mercurial > pub > Impl
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 |
