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