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