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