Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Navigator/Builder.pm @ 246:2746a8e5a6c4
Fixed regressions in DOM due previous refactorings
Fixed ObjectToDOM transformation to handle a schema with mixed node types
author | sergey |
---|---|
date | Tue, 30 Oct 2012 01:17:31 +0400 |
parents | b8c724f6de36 |
children | 129e48bb5afb |
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); |
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}) { | |
49 $node = $this->{$Document} = $this->{$_docClass}->new(nodeName => $nodeName,%props); | |
106 | 50 $this->_initNavigator($node); |
49 | 51 } else { |
206 | 52 die new IMPL::InvalidOperationException('Can create a second top level element') unless $this->Current; |
49 | 53 $node = $this->{$Document}->Create($nodeName,$class,\%props); |
106 | 54 $this->Current->appendChild($node); |
55 $this->internalNavigateNodeSet($node); | |
49 | 56 } |
57 | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
58 if (@errors) { |
194 | 59 $this->BuildErrors->Append( |
60 map { | |
61 IMPL::DOM::Schema::ValidationError->new( | |
236 | 62 node => $node, |
63 source => $schemaSource, | |
64 schema => $schemaNode, | |
65 message => $schemaNode->messageInflateError, | |
66 error => $_ | |
194 | 67 ) |
68 } @errors | |
69 ); | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
70 } |
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
71 |
49 | 72 return $node; |
73 } else { | |
236 | 74 die new IMPL::InvalidOperationException("The specified node is undefined", $nodeName) |
238 | 75 if !$this->ignoreUndefined; |
236 | 76 return; |
49 | 77 } |
78 } | |
79 | |
103 | 80 sub inflateProperties { |
194 | 81 my ($this,$schemaNode,$refProps) = @_; |
82 my @errors; | |
83 foreach my $schemaProp ( $schemaNode->selectNodes('Property') ) { | |
84 next if not exists $refProps->{$schemaProp->name}; | |
85 my $result = eval {$schemaProp->inflateValue($refProps->{$schemaProp->name}) }; | |
86 if (my $e = $@) { | |
87 push @errors, $e; | |
88 } else { | |
89 $refProps->{$schemaProp->name} = $result; | |
90 } | |
91 } | |
92 return @errors; | |
103 | 93 } |
94 | |
95 sub inflateValue { | |
194 | 96 my ($this,$value,$node) = @_; |
97 | |
98 $node ||= $this->Current; | |
99 | |
100 my $nodeSchema = $this->{$_schemaNavi}->Current; | |
101 | |
102 my $result = eval { $nodeSchema->inflateValue($value) }; | |
103 if (my $e=$@) { | |
104 $this->BuildErrors->Append(new IMPL::DOM::Schema::ValidationError( | |
246 | 105 schema => $nodeSchema, |
106 node => $node, | |
107 error => $e, | |
108 message => $nodeSchema->messageInflateError, | |
109 source => $this->{$_schemaNavi}->SourceSchemaNode | |
194 | 110 )); |
111 return $value; | |
112 } else { | |
113 return $result; | |
114 } | |
103 | 115 } |
116 | |
49 | 117 sub Back { |
118 my ($this) = @_; | |
119 | |
120 $this->{$_schemaNavi}->SchemaBack(); | |
106 | 121 $this->SUPER::Back(); |
49 | 122 } |
123 | |
112 | 124 sub saveState { |
194 | 125 my ($this) = @_; |
126 | |
127 $this->{$_schemaNavi}->saveState; | |
128 $this->SUPER::saveState; | |
112 | 129 } |
130 | |
131 sub restoreState { | |
194 | 132 my ($this) = @_; |
133 | |
134 $this->{$_schemaNavi}->restoreState; | |
135 $this->SUPER::restoreState; | |
112 | 136 } |
137 | |
236 | 138 #compatibility |
139 sub buildErrors { | |
140 goto &BuildErrors; | |
141 } | |
142 | |
49 | 143 1; |
144 | |
145 __END__ | |
64 | 146 |
49 | 147 =pod |
148 | |
64 | 149 =head1 NAME |
150 | |
180 | 151 C< IMPL::DOM::Navigator::Builder > - Навигатор, строящий документ по указанной схеме. |
64 | 152 |
49 | 153 =head1 SYNOPSIS |
154 | |
64 | 155 =begin code |
156 | |
49 | 157 my $builder = new IMPL::DOM::Navigator::Builder(new MyApp::Document,$schema); |
158 my $reader = new IMPL::DOM::XMLReader(Navigator => $builder); | |
159 | |
160 $reader->ParseFile("document.xml"); | |
161 | |
162 my @errors = $schema->Validate($builder->Document); | |
163 | |
64 | 164 =end code |
165 | |
49 | 166 =head1 DESCRIPTION |
167 | |
180 | 168 Построитель DOM документов по указанной схеме. Обычно используется в связке |
169 с объектами для чтения такими как C<IMPL::DOM::XMLReader>. | |
49 | 170 |
171 =head1 METHODS | |
172 | |
173 =over | |
174 | |
64 | 175 =item C< CTOR($classDocument,$schema) > |
49 | 176 |
180 | 177 Создает новый объект, принимает на вход класс документа (или фабрику, например |
178 L<IMPL::Object::Factory>) и схему. В процессе процедуры построения документа | |
179 будет создан объект документа. | |
49 | 180 |
64 | 181 =item C< NavigateCreate($nodeName,\%props) > |
49 | 182 |
180 | 183 Создает новый узел с указанным именем и переходит в него. В случае если в схеме |
184 подходящий узел не найден, то вызывается исключение. | |
49 | 185 |
180 | 186 При этом по имени узла ищется его схема, после чего определяется класс для |
187 создания экземпляра и созданный узел доавляется в документ. При создании | |
188 нового узла используется метод документа C<< IMPL::DOM::Document->Create >> | |
49 | 189 |
180 | 190 Свойства узла передаются при создании через параметр C<%props>, но имя создаваемого |
191 узла НЕ может быть переопределено свойством C<nodeName>, оно будет проигнорировано. | |
64 | 192 |
193 =item C< Document > | |
194 | |
180 | 195 Свойство, которое содержит документ по окончании процедуры построения. |
49 | 196 |
197 =back | |
198 | |
199 =cut |