comparison lib/IMPL/DOM/Schema.pm @ 407:c6e90e02dd17 ref20150831

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