comparison lib/IMPL/DOM/Transform/ObjectToDOM.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::DOM::Transform::ObjectToDOM;
2 use strict;
3
4 use IMPL::Const qw(:prop :access);
5 use IMPL::declare {
6 require => {
7 PropertyInfo => 'IMPL::Class::PropertyInfo',
8 Builder => 'IMPL::DOM::Navigator::Builder',
9 Exception => 'IMPL::Exception',
10 ArgumentException => '-IMPL::InvalidArgumentException',
11 OperationException => '-IMPL::InvalidOperationException'
12 },
13 base => [
14 'IMPL::Transform' => sub {
15 -plain => 'TransformPlain',
16 HASH => 'TransformHash',
17 -default => 'TransformDefault'
18 }
19 ],
20 props => [
21 documentSchema => PROP_RO,
22 _schema => PROP_RW,
23 _navi => PROP_RW
24 ]
25 };
26
27 use constant {
28 SchemaNode => 'IMPL::DOM::Schema::Node',
29 ComplexNode => 'IMPL::DOM::Schema::ComplexNode'
30 };
31
32 sub CTOR {
33 my ($this,$docName,$docSchema,$transforms) = @_;
34
35 my $docNodeSchema = $docSchema->selectSingleNode(sub { $_->isa(SchemaNode) and $_->name eq $docName } )
36 or die OperationException->new("Can't find a node schema for the document '$docName'");
37
38 my $docClass = ($docNodeSchema->can('nativeType') ? $docNodeSchema->nativeType : undef) || 'IMPL::DOM::Document';
39
40 $this->documentSchema($docNodeSchema);
41
42 $this->_navi(
43 Builder->new(
44 $docClass,
45 $docSchema,
46 ignoreUndefined => 1
47 )
48 );
49 $this->_schema($docSchema);
50
51 $this->_navi->NavigateCreate($docName);
52 $this->currentNode->nodeProperty(schemaDocument => $docSchema);
53 }
54
55 sub TransformPlain {
56 my ($this,$data) = @_;
57
58 $this->_navi->Current->nodeValue( $data );
59 return $this->_navi->Current;
60 }
61
62 sub currentNode {
63 shift->_navi->Current;
64 }
65
66 sub TransformHash {
67 my ($this,$data) = @_;
68
69 die ArgumentException->new(data => 'A HASH reference is required')
70 unless ref $data eq 'HASH';
71
72 return $this->StoreObject($this->currentNode,$data)
73 if !$this->currentNode->schemaType->isa(ComplexNode);
74
75 KEYLOOP: foreach my $key (keys %$data) {
76 my $value = $data->{$key};
77
78 if (ref $value eq 'ARRAY') {
79 foreach my $subval (grep $_, @$value) {
80
81 $this->_navi->saveState();
82
83 my $node = $this->_navi->NavigateCreate($key);
84
85 unless(defined $node) {
86 #$this->_navi->Back();
87 $this->_navi->restoreState();
88 next KEYLOOP;
89 }
90
91 $this->_navi->applyState();
92
93 $this->Transform($subval);
94
95 $this->_navi->Back();
96 }
97 } else {
98 $this->_navi->saveState();
99 my $node = $this->_navi->NavigateCreate($key);
100
101 unless(defined $node) {
102 #$this->_navi->Back();
103 $this->_navi->restoreState();
104 next KEYLOOP;
105 }
106
107 $this->_navi->applyState();
108
109 $this->Transform($value);
110
111 $this->_navi->Back();
112 }
113 }
114 return $this->_navi->Current;
115 }
116
117 # this method handles situatuions when a complex object must be stored in a
118 # simple node.
119 sub StoreObject {
120 my ($this,$node,$data) = @_;
121
122 $node->nodeValue($data);
123
124 return $node;
125 }
126
127 sub TransformDefault {
128 my ($this,$data) = @_;
129
130 return $this->StoreObject($this->currentNode,$data)
131 if !$this->currentNode->schemaType->isa(ComplexNode);
132
133 if ( ref $data and eval { $data->can('GetMeta') } ) {
134 my %props = map {
135 $_->name, 1
136 } $data->GetMeta(PropertyInfo, sub { $_->access == ACCESS_PUBLIC }, 1 );
137
138
139 my %values = map {
140 $_,
141 scalar($data->$_())
142 } keys %props;
143
144 return $this->Transform(\%values);
145 } else {
146 die OperationException->new("Don't know how to transform $data");
147 }
148
149 return $this->_navi->Current;
150 }
151
152 1;
153
154 __END__
155
156 =pod
157
158 =head1 NAME
159
160 C<IMPL::DOM::Transform::ObjectToDOM> -преобразование объекта в DOM документ.
161
162 =head1 SYNOPSIS
163
164 =begin code
165
166 use IMPL::require {
167 Schema => 'IMPL::DOM::Schema',
168 Config => 'IMPL::Config'
169 }
170
171 my $data = {
172 id => '12313-232',
173 name => 'Peter',
174 age => 20
175 };
176
177 my $schema = Schema->LoadSchema(Config->AppBase('schemas','person.xml'));
178 my $transorm = IMPL::DOM::Transform::ObjectToDOM->new('edit', $schema);
179
180 my $form = $transform->Transform($data);
181
182 my @errors;
183
184 push @errors, $schema->Validate($doc);
185
186 =end code
187
188 =head1 DESCRIPTION
189
190 Наследует C<IMPL::Transform>. Определяет базовые преобразования для хешей и
191 объектов, поддерживающих метаданные.
192
193 Результатом выполнения преобразования является DOM документ. При построении
194 документа используется навигатор C<IMPL::DOM::Navigator::Builder> для
195 сопоставления схемы и свойств преобразуемого объекта. Элементы полученного
196 документа имеют ссылки на соответствующие им элементы схемы.
197
198 После того, как документ построен и преобразование будет очищено, не останется
199 объектов, которые бы ссылались на документ со схемой, поскольку элементы схемы
200 имеют слабые ссылки на саму схему и не могут предотвратить ее удаление.
201 Для предотвращения очитски документа схемы, ссылка на него сохраняется в
202 атрибуте документа C<schemaDocument>, что обеспечит жизнь схемы на протяжении
203 жизни документа.
204
205 Преобразование происходит рекурсивно, сначала используется метод
206 C<NavigateCreate> для создания элемента соответсвующего свойству объекта,
207 затем вызывается метод C<Transform> для преобразования значения свойства, при
208 этом C<currentNode> указывает на только что созданный элемент документа.
209
210 Для изменения поведения преобразования можно добавлять новые обработчики, как
211 в случае со стандартным преобразованием, а также можно унаследовать текущий
212 класс для переопределения его некоторых методов.
213
214 =head1 MEMBERS
215
216 =head2 C<CTOR($docName,$schema)>
217
218 Создает преобразование, при этом будет создан документ состоящий только из
219 корневого элемента с именем C<$docName> и будет найдена подходящий для него
220 элемент схемы C<$schema>.
221
222 =over
223
224 =item * C<$docName>
225
226 Имя корневого узла документа, которое будет использовано для поиска
227 соответствующего элемента схемы C<$schema>
228
229 =item * C<$schema>
230
231 Схема, содержащая описание документа. Если в данной схеме нет описания корневого
232 элемента с именем C<$docName>, будет вызвано исключение.
233
234 =back
235
236 =head2 C<[get]documentSchema>
237
238 Элемент схемы C<ComplexNode> соответствующий документу. Определяется в
239 конструкторе исходя из имени документа.
240
241 =head2 C<[get]currentNode>
242
243 Текущий элемент документа. После создания преобразования - это сам документ.
244 Данное свойство использется внутри преобразования для работы с текущим
245 элементом.
246
247 =head2 C<[virtual]StoreObject($node,$data)>
248
249 Метод, который вызывается преобразованием в случае если текущий узел документа
250 является простым, а значени которое ему соответсвует является объектом (ссылкой).
251
252 По-умолчанию будет выполнено присваивание C<< $node->nodeValue($data) >>, однако
253 это можно заменить, например, на преобразование в строку.
254
255 =cut