Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Navigator/Builder.pm @ 250:129e48bb5afb
DOM refactoring
ObjectToDOM methods are virtual
QueryToDOM uses inflators
Fixed transform for the complex values in the ObjectToDOM
QueryToDOM doesn't allow to use complex values (HASHes) as values for nodes (overpost problem)
author | sergey |
---|---|
date | Wed, 07 Nov 2012 04:17:53 +0400 |
parents | 2746a8e5a6c4 |
children | 4ddb27ff4a0b |
rev | line source |
---|---|
49 | 1 package IMPL::DOM::Navigator::Builder; |
2 use strict; | |
3 use warnings; | |
4 | |
236 | 5 use IMPL::Const qw(:prop); |
6 | |
165 | 7 use parent qw(IMPL::DOM::Navigator); |
49 | 8 use IMPL::Class::Property; |
9 use IMPL::Class::Property::Direct; | |
10 require IMPL::DOM::Navigator::SchemaNavigator; | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
11 require IMPL::DOM::Schema::ValidationError; |
106 | 12 use IMPL::DOM::Document; |
49 | 13 |
14 BEGIN { | |
236 | 15 private _direct property _schemaNavi => PROP_RW; |
16 private _direct property _docClass => PROP_RW; | |
17 public _direct property BuildErrors => PROP_RO | PROP_LIST; | |
18 public _direct property Document => PROP_RO; | |
19 public _direct property ignoreUndefined => PROP_RO; | |
49 | 20 } |
21 | |
106 | 22 our %CTOR = ( |
112 | 23 'IMPL::DOM::Navigator' => sub { IMPL::DOM::Document->Empty; } |
106 | 24 ); |
25 | |
49 | 26 sub CTOR { |
236 | 27 my ($this,$docClass,$schema,%opts) = @_; |
49 | 28 |
29 $this->{$_docClass} = $docClass; | |
30 $this->{$_schemaNavi} = $schema ? IMPL::DOM::Navigator::SchemaNavigator->new($schema) : undef; | |
236 | 31 |
32 $this->{$ignoreUndefined} = $opts{ignoreUndefined} if $opts{ignoreUndefined}; | |
49 | 33 } |
34 | |
35 sub NavigateCreate { | |
36 my ($this,$nodeName,%props) = @_; | |
37 | |
38 if (my $schemaNode = $this->{$_schemaNavi}->NavigateName($nodeName)) { | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
39 my $class = $schemaNode->can('nativeType') ? $schemaNode->nativeType || 'IMPL::DOM::Node' : 'IMPL::DOM::Node'; |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
40 |
148
e6447ad85cb4
DOM objects now have a schema and schemaSource properties
wizard
parents:
113
diff
changeset
|
41 my $schemaSource = $this->{$_schemaNavi}->SourceSchemaNode; |
e6447ad85cb4
DOM objects now have a schema and schemaSource properties
wizard
parents:
113
diff
changeset
|
42 |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
43 my @errors = $this->inflateProperties($schemaNode,\%props); |
250 | 44 |
148
e6447ad85cb4
DOM objects now have a schema and schemaSource properties
wizard
parents:
113
diff
changeset
|
45 $props{schema} = $schemaNode; |
e6447ad85cb4
DOM objects now have a schema and schemaSource properties
wizard
parents:
113
diff
changeset
|
46 $props{schemaSource} = $schemaSource; |
49 | 47 |
48 my $node; | |
49 if (! $this->{$Document}) { | |
50 $node = $this->{$Document} = $this->{$_docClass}->new(nodeName => $nodeName,%props); | |
106 | 51 $this->_initNavigator($node); |
49 | 52 } else { |
250 | 53 die new IMPL::InvalidOperationException('Can\'t create a second top level element') unless $this->Current; |
49 | 54 $node = $this->{$Document}->Create($nodeName,$class,\%props); |
106 | 55 $this->Current->appendChild($node); |
56 $this->internalNavigateNodeSet($node); | |
49 | 57 } |
58 | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
59 if (@errors) { |
194 | 60 $this->BuildErrors->Append( |
61 map { | |
62 IMPL::DOM::Schema::ValidationError->new( | |
236 | 63 node => $node, |
64 source => $schemaSource, | |
65 schema => $schemaNode, | |
66 message => $schemaNode->messageInflateError, | |
67 error => $_ | |
194 | 68 ) |
69 } @errors | |
70 ); | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
71 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
72 |
49 | 73 return $node; |
74 } else { | |
236 | 75 die new IMPL::InvalidOperationException("The specified node is undefined", $nodeName) |
238 | 76 if !$this->ignoreUndefined; |
236 | 77 return; |
49 | 78 } |
79 } | |
80 | |
103 | 81 sub inflateProperties { |
194 | 82 my ($this,$schemaNode,$refProps) = @_; |
83 my @errors; | |
84 foreach my $schemaProp ( $schemaNode->selectNodes('Property') ) { | |
85 next if not exists $refProps->{$schemaProp->name}; | |
86 my $result = eval {$schemaProp->inflateValue($refProps->{$schemaProp->name}) }; | |
87 if (my $e = $@) { | |
88 push @errors, $e; | |
89 } else { | |
90 $refProps->{$schemaProp->name} = $result; | |
91 } | |
92 } | |
93 return @errors; | |
103 | 94 } |
95 | |
96 sub inflateValue { | |
250 | 97 my ($this,$value,$node,$strict) = @_; |
194 | 98 |
250 | 99 $strict ||= 0; |
194 | 100 $node ||= $this->Current; |
101 | |
102 my $nodeSchema = $this->{$_schemaNavi}->Current; | |
103 | |
104 my $result = eval { $nodeSchema->inflateValue($value) }; | |
105 if (my $e=$@) { | |
106 $this->BuildErrors->Append(new IMPL::DOM::Schema::ValidationError( | |
246 | 107 schema => $nodeSchema, |
108 node => $node, | |
109 error => $e, | |
110 message => $nodeSchema->messageInflateError, | |
111 source => $this->{$_schemaNavi}->SourceSchemaNode | |
194 | 112 )); |
250 | 113 return $strict ? undef : $value ; |
194 | 114 } else { |
115 return $result; | |
116 } | |
103 | 117 } |
118 | |
49 | 119 sub Back { |
120 my ($this) = @_; | |
121 | |
122 $this->{$_schemaNavi}->SchemaBack(); | |
106 | 123 $this->SUPER::Back(); |
49 | 124 } |
125 | |
112 | 126 sub saveState { |
194 | 127 my ($this) = @_; |
128 | |
129 $this->{$_schemaNavi}->saveState; | |
130 $this->SUPER::saveState; | |
112 | 131 } |
132 | |
133 sub restoreState { | |
194 | 134 my ($this) = @_; |
135 | |
136 $this->{$_schemaNavi}->restoreState; | |
137 $this->SUPER::restoreState; | |
112 | 138 } |
139 | |
236 | 140 #compatibility |
141 sub buildErrors { | |
142 goto &BuildErrors; | |
143 } | |
144 | |
250 | 145 sub document { |
146 goto &Document; | |
147 } | |
148 | |
49 | 149 1; |
150 | |
151 __END__ | |
64 | 152 |
49 | 153 =pod |
154 | |
64 | 155 =head1 NAME |
156 | |
180 | 157 C< IMPL::DOM::Navigator::Builder > - Навигатор, строящий документ по указанной схеме. |
64 | 158 |
49 | 159 =head1 SYNOPSIS |
160 | |
64 | 161 =begin code |
162 | |
49 | 163 my $builder = new IMPL::DOM::Navigator::Builder(new MyApp::Document,$schema); |
164 my $reader = new IMPL::DOM::XMLReader(Navigator => $builder); | |
165 | |
166 $reader->ParseFile("document.xml"); | |
167 | |
168 my @errors = $schema->Validate($builder->Document); | |
169 | |
64 | 170 =end code |
171 | |
49 | 172 =head1 DESCRIPTION |
173 | |
180 | 174 Построитель DOM документов по указанной схеме. Обычно используется в связке |
175 с объектами для чтения такими как C<IMPL::DOM::XMLReader>. | |
49 | 176 |
250 | 177 =head1 MEMBERS |
49 | 178 |
250 | 179 =head2 C< CTOR($classDocument,$schema, %opts) > |
49 | 180 |
180 | 181 Создает новый объект, принимает на вход класс документа (или фабрику, например |
182 L<IMPL::Object::Factory>) и схему. В процессе процедуры построения документа | |
183 будет создан объект документа. | |
49 | 184 |
250 | 185 Необязательные именованные параметры |
186 | |
187 =over | |
188 | |
189 =item C<ignoreUndefined> | |
190 | |
191 C<NavigateCreate> не будет вызывать исключение, если запрашиваемый узел не | |
192 найден в схеме, но будет возвращать C<undef>. | |
193 | |
194 =back | |
195 | |
196 =head2 C< NavigateCreate($nodeName,%props) > | |
49 | 197 |
180 | 198 Создает новый узел с указанным именем и переходит в него. В случае если в схеме |
250 | 199 подходящий узел не найден, то вызывается исключение или будет возвращено |
200 C<undef> см. C<ignoreUndefined>. | |
49 | 201 |
180 | 202 При этом по имени узла ищется его схема, после чего определяется класс для |
250 | 203 создания экземпляра узла и созданный узел доавляется в документ. При создании |
180 | 204 нового узла используется метод документа C<< IMPL::DOM::Document->Create >> |
49 | 205 |
250 | 206 Свойства узла передаются при создании через параметр C<%props>, но имя |
207 создаваемого узла НЕ может быть переопределено свойством C<nodeName>, оно будет | |
208 проигнорировано. | |
64 | 209 |
250 | 210 Свойства узла будут преобразованы при помощи заданных в схеме заполнителей |
211 C<inflator>. | |
212 | |
213 =head2 C<[get]document > | |
64 | 214 |
180 | 215 Свойство, которое содержит документ по окончании процедуры построения. |
49 | 216 |
250 | 217 =head2 C<[get]buildErrors> |
218 | |
219 Ошибки, возникшие в процессе построения документа. | |
220 | |
221 =head2 C<[get]ignoreUndefined> | |
222 | |
223 Опция, заданная при создании построителя, отвечающая за обработку узлов | |
224 не найденных в схеме. | |
49 | 225 |
226 =cut |