Mercurial > pub > Impl
annotate Lib/IMPL/DOM/Schema.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 | 0f59b2de72af |
rev | line source |
---|---|
49 | 1 package IMPL::DOM::Schema; |
2 use strict; | |
3 use warnings; | |
4 | |
230 | 5 use IMPL::require { |
6 ComplexNode => 'IMPL::DOM::Schema::ComplexNode', | |
7 ComplexType => 'IMPL::DOM::Schema::ComplexType', | |
8 SimpleNode => 'IMPL::DOM::Schema::SimpleNode', | |
9 SimpleType => 'IMPL::DOM::Schema::SimpleType', | |
10 Node => 'IMPL::DOM::Schema::Node', | |
11 AnyNode => 'IMPL::DOM::Schema::AnyNode', | |
12 NodeList => 'IMPL::DOM::Schema::NodeList', | |
13 NodeSet => 'IMPL::DOM::Schema::NodeSet', | |
14 Property => 'IMPL::DOM::Schema::Property', | |
15 SwitchNode => 'IMPL::DOM::Schema::SwitchNode', | |
16 Validator => 'IMPL::DOM::Schema::Validator', | |
17 Builder => 'IMPL::DOM::Navigator::Builder', | |
18 XMLReader => 'IMPL::DOM::XMLReader', | |
19 InflateFactory => 'IMPL::DOM::Schema::InflateFactory', | |
232 | 20 Loader => 'IMPL::Code::Loader' |
230 | 21 }; |
49 | 22 |
166 | 23 use parent qw(IMPL::DOM::Document); |
49 | 24 use IMPL::Class::Property; |
25 use IMPL::Class::Property::Direct; | |
101 | 26 use File::Spec; |
49 | 27 |
28 our %CTOR = ( | |
29 'IMPL::DOM::Document' => sub { nodeName => 'schema' } | |
30 ); | |
31 | |
32 BEGIN { | |
33 private _direct property _TypesMap => prop_all; | |
101 | 34 public _direct property baseDir => prop_all; |
49 | 35 public _direct property BaseSchemas => prop_get | owner_set; |
36 } | |
37 | |
230 | 38 my $validatorLoader = Loader->new(prefix => Validator, verifyNames => 1); |
39 | |
250 | 40 #TODO rename and remove |
49 | 41 sub resolveType { |
246 | 42 $_[0]->{$_TypesMap}->{$_[1]} or die IMPL::KeyNotFoundException->new($_[1]); |
49 | 43 } |
44 | |
102 | 45 sub CTOR { |
194 | 46 my ($this,%args) = @_; |
47 | |
48 $this->{$baseDir} = ($args{baseDir} || '.'); | |
102 | 49 } |
50 | |
250 | 51 # compat |
52 sub ResolveType { | |
53 goto &resolveType | |
54 } | |
55 | |
49 | 56 sub Create { |
57 my ($this,$nodeName,$class,$refArgs) = @_; | |
58 | |
100 | 59 die new IMPL::Exception('Invalid node class') unless $class->isa('IMPL::DOM::Node'); |
49 | 60 |
101 | 61 if ($class->isa('IMPL::DOM::Schema::Validator')) { |
230 | 62 $class = $validatorLoader->GetFullName($nodeName); |
194 | 63 unless (eval {$class->can('new')}) { |
232 | 64 eval { |
65 $validatorLoader->Require($nodeName); | |
66 }; | |
67 my $e = $@; | |
194 | 68 die new IMPL::Exception("Invalid validator",$class,$e) if $e; |
69 } | |
101 | 70 } |
71 | |
72 return $this->SUPER::Create($nodeName,$class,$refArgs); | |
49 | 73 } |
74 | |
75 sub Process { | |
76 my ($this) = @_; | |
77 | |
101 | 78 # process instructions |
79 $this->Include($_) foreach map $_->nodeProperty('source'), $this->selectNodes('Include'); | |
80 | |
81 # build types map | |
49 | 82 $this->{$_TypesMap} = { map { $_->type, $_ } $this->selectNodes(sub { $_[0]->nodeName eq 'ComplexType' || $_[0]->nodeName eq 'SimpleType' } ) }; |
83 } | |
84 | |
101 | 85 sub Include { |
194 | 86 my ($this,$file) = @_; |
87 | |
88 my $schema = $this->LoadSchema(File::Spec->catfile($this->baseDir, $file)); | |
89 | |
90 $this->appendRange( $schema->childNodes ); | |
101 | 91 } |
92 | |
93 sub LoadSchema { | |
194 | 94 my ($this,$file) = @_; |
95 | |
96 $file = File::Spec->rel2abs($file); | |
97 | |
98 my $class = ref $this || $this; | |
99 | |
232 | 100 my $reader = XMLReader->new( |
230 | 101 Navigator => Builder->new( |
194 | 102 $class, |
103 $class->MetaSchema | |
104 ), | |
105 SkipWhitespace => 1 | |
106 ); | |
107 | |
108 $reader->ParseFile($file); | |
109 | |
110 my $schema = $reader->Navigator->Document; | |
111 | |
112 my ($vol,$dir) = File::Spec->splitpath($file); | |
113 | |
114 $schema->baseDir($dir); | |
115 | |
116 my @errors = $class->MetaSchema->Validate($schema); | |
117 | |
245 | 118 die new IMPL::Exception("Schema is invalid",$file,map( $_->message, @errors ) ) if @errors; |
194 | 119 |
120 $schema->Process; | |
121 | |
122 return $schema; | |
101 | 123 } |
124 | |
49 | 125 sub Validate { |
126 my ($this,$node) = @_; | |
127 | |
230 | 128 if ( my ($schemaNode) = $this->selectNodes(sub { $_->isa(Node) and $_[0]->name eq $node->nodeName })) { |
49 | 129 $schemaNode->Validate($node); |
130 } else { | |
126 | 131 return new IMPL::DOM::Schema::ValidationError(Node => $node, Message=> "A specified document (%Node.nodeName%) doesn't match the schema"); |
49 | 132 } |
133 } | |
134 | |
135 my $schema; | |
136 | |
137 sub MetaSchema { | |
138 | |
139 return $schema if $schema; | |
140 | |
232 | 141 $schema = __PACKAGE__->new(); |
49 | 142 |
143 $schema->appendRange( | |
230 | 144 ComplexNode->new(name => 'schema')->appendRange( |
145 NodeSet->new()->appendRange( | |
146 Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), | |
147 Node->new(name => 'ComplexType', type => 'ComplexType', minOccur => 0, maxOccur=>'unbounded'), | |
148 Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), | |
149 Node->new(name => 'SimpleType', type => 'SimpleType', minOccur => 0, maxOccur=>'unbounded'), | |
150 Node->new(name => 'Node', type => 'Node', minOccur => 0, maxOccur=>'unbounded'), | |
151 SimpleNode->new(name => 'Include', minOccur => 0, maxOccur=>'unbounded')->appendRange( | |
152 Property->new(name => 'source') | |
49 | 153 ) |
154 ), | |
155 ), | |
230 | 156 ComplexType->new(type => 'NodeSet', nativeType => 'IMPL::DOM::Schema::NodeSet')->appendRange( |
157 NodeSet->new()->appendRange( | |
158 Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), | |
159 Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), | |
160 Node->new(name => 'Node', type=>'Node', minOccur => 0, maxOccur=>'unbounded'), | |
161 SwitchNode->new(minOccur => 0, maxOccur => 1)->appendRange( | |
162 Node->new(name => 'AnyNode', type => 'AnyNode'), | |
163 Node->new(name => 'SwitchNode',type => 'SwitchNode') | |
49 | 164 ) |
165 ) | |
166 ), | |
230 | 167 ComplexType->new(type => 'SwitchNode', nativeType => 'IMPL::DOM::Schema::SwitchNode')->appendRange( |
168 NodeSet->new()->appendRange( | |
169 Node->new(name => 'ComplexNode', type=>'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), | |
170 Node->new(name => 'SimpleNode', type=>'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), | |
171 Node->new(name => 'Node', type=>'Node', minOccur => 0, maxOccur=>'unbounded'), | |
49 | 172 ) |
173 ), | |
230 | 174 ComplexType->new(type => 'NodeList', nativeType => 'IMPL::DOM::Schema::NodeList')->appendRange( |
175 NodeSet->new()->appendRange( | |
176 Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), | |
177 Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), | |
178 Node->new(name => 'SwitchNode',type => 'SwitchNode', minOccur => 0, maxOccur=>'unbounded'), | |
179 Node->new(name => 'Node', type => 'Node', minOccur => 0, maxOccur=>'unbounded'), | |
180 Node->new(name => 'AnyNode', type => 'AnyNode', minOccur => 0, maxOccur=>'unbounded'), | |
49 | 181 ) |
182 ), | |
230 | 183 ComplexType->new(type => 'ComplexType', nativeType => 'IMPL::DOM::Schema::ComplexType')->appendRange( |
184 NodeList->new()->appendRange( | |
185 SwitchNode->new()->appendRange( | |
186 Node->new(name => 'NodeSet', type => 'NodeSet'), | |
187 Node->new(name => 'NodeList',type => 'NodeList'), | |
49 | 188 ), |
230 | 189 Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), |
190 AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') | |
49 | 191 ), |
230 | 192 Property->new(name => 'type') |
49 | 193 ), |
230 | 194 ComplexType->new(type => 'ComplexNode', nativeType => 'IMPL::DOM::Schema::ComplexNode')->appendRange( |
195 NodeList->new()->appendRange( | |
196 SwitchNode->new()->appendRange( | |
197 Node->new(name => 'NodeSet', type => 'NodeSet'), | |
198 Node->new(name => 'NodeList',type => 'NodeList'), | |
49 | 199 ), |
230 | 200 Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), |
201 AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') | |
49 | 202 ), |
230 | 203 Property->new(name => 'name') |
49 | 204 ), |
230 | 205 ComplexType->new(type => 'SimpleType', nativeType => 'IMPL::DOM::Schema::SimpleType')->appendRange( |
206 NodeList->new()->appendRange( | |
207 Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), | |
208 AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') | |
49 | 209 ), |
230 | 210 Property->new(name => 'type'), |
232 | 211 Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') |
49 | 212 ), |
230 | 213 ComplexType->new(type => 'SimpleNode', nativeType => 'IMPL::DOM::Schema::SimpleNode')->appendRange( |
214 NodeList->new()->appendRange( | |
215 Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), | |
216 AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') | |
49 | 217 ), |
230 | 218 Property->new(name => 'name'), |
219 Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') | |
49 | 220 ), |
230 | 221 ComplexType->new(type => 'Validator', nativeType => 'IMPL::DOM::Schema::Validator')->appendRange( |
222 NodeList->new()->appendRange( | |
223 AnyNode->new(maxOccur => 'unbounded', minOccur => 0) | |
100 | 224 ) |
102 | 225 ), |
230 | 226 ComplexType->new(type => 'Property', nativeType => 'IMPL::DOM::Schema::Property' )->appendRange( |
227 NodeList->new()->appendRange( | |
228 AnyNode->new(maxOccur => 'unbounded', minOccur => 0) | |
194 | 229 ), |
230 | 230 Property->new(name => 'name'), |
231 Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
232 ), |
230 | 233 SimpleType->new(type => 'Node', nativeType => 'IMPL::DOM::Schema::Node')->appendRange( |
234 Property->new(name => 'name'), | |
235 Property->new(name => 'type') | |
104
196bf443b5e1
DOM::Schema RC0 inflators support, validation and some other things,
wizard
parents:
103
diff
changeset
|
236 ), |
230 | 237 SimpleType->new(type => 'AnyNode', nativeType => 'IMPL::DOM::Schema::AnyNode') |
49 | 238 ); |
239 | |
240 $schema->Process; | |
241 | |
242 return $schema; | |
243 } | |
244 | |
245 1; | |
246 | |
247 __END__ | |
248 | |
249 =pod | |
250 | |
98 | 251 =head1 NAME |
252 | |
180 | 253 C<IMPL::DOM::Schema> - Схема документа. |
98 | 254 |
49 | 255 =head1 DESCRIPTION |
256 | |
166 | 257 C<use parent qw(IMPL::DOM::Document)> |
98 | 258 |
180 | 259 DOM схема - это документ, состоящий из определенных узлов, описывающая структуру |
260 других документов. | |
49 | 261 |
262 =head1 METHODS | |
263 | |
264 =over | |
265 | |
266 =item C<< $obj->Process() >> | |
267 | |
180 | 268 Обновляет таблицу типов из содержимого. |
49 | 269 |
270 =item C<< $obj->ResolveType($typeName) >> | |
271 | |
180 | 272 Возвращает схему типа c именем C<$typeName>. |
49 | 273 |
274 =back | |
275 | |
276 =head1 META SCHEMA | |
277 | |
180 | 278 Схема для описания схемы, эта схема используется для постороения других схем, выглядит приблизительно так |
49 | 279 |
98 | 280 =begin code xml |
281 | |
49 | 282 <schema> |
283 <ComplexNode name="schema"> | |
284 <NodeSet> | |
285 <Node minOcuur="0" maxOccur="unbounded" name="ComplexNode" type="ComplexNode"/> | |
286 <Node minOcuur="0" maxOccur="unbounded" name="SimpleNode" type="SimpleNode"/> | |
287 <Node minOcuur="0" maxOccur="unbounded" name="ComplexType" type="ComplexType"/> | |
288 <Node minOcuur="0" maxOccur="unbounded" name="SimpleType" type="SimpleType"/> | |
289 <SimpleNode minOcuur="0" maxOccur="unbounded" name="Node"/> | |
290 <SimpleNode minOcuur="0" maxOccur="unbounded" name="Include"/> | |
291 </NodeSet> | |
292 </ComplexNode> | |
293 | |
294 <ComplexType type="NodeContainer"> | |
295 <NodeSet> | |
296 <Node minOcuur="0" maxOccur="unbounded" name="ComplexNode" type="ComplexNode"/> | |
297 <Node minOcuur="0" maxOccur="unbounded" name="SimpleNode" type="SimpleNode"/> | |
298 <SimpleNode minOcuur="0" maxOccur="unbounded" name="Node"/> | |
299 </NodeSet> | |
300 </ComplexType> | |
301 | |
302 <ComplexType type="ComplexType"> | |
303 <NodeList> | |
304 <Node name="NodeSet" type="NodeContainer" minOcuur=0/> | |
305 <Node name="NodeList" type="NodeContainer" minOccur=0/> | |
306 <AnyNode minOccur="0" maxOccur="unbounded" type="Validator"/> | |
307 </NodeList> | |
308 </ComplexType> | |
309 | |
310 <ComplexType type="ComplexNode"> | |
311 <NodeList> | |
312 <Node name="NodeSet" type="NodeContainer" minOcuur=0/> | |
313 <Node name="NodeList" type="NodeContainer" minOccur=0/> | |
314 <AnyNode minOccur="0" maxOccur="unbounded" type="Validator"/> | |
315 </NodeList> | |
316 </ComplexType> | |
317 | |
318 <ComplexType type="SimpleNode"> | |
319 <NodeSet> | |
320 <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/> | |
321 </NodeSet> | |
322 </ComplexType> | |
323 | |
324 <ComplexType type="SimpleType"> | |
325 <NodeSet> | |
326 <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/> | |
327 </NodeSet> | |
328 </ComplexType> | |
329 | |
330 <ComplexType type="Validator"> | |
331 <NodeSet> | |
332 <AnyNode minOccur=0 maxOccur="unbounded"/> | |
333 </NodeSet> | |
334 </ComplexType> | |
335 | |
336 </schema> | |
337 | |
98 | 338 =end code xml |
339 | |
49 | 340 =cut |