Mercurial > pub > Impl
view Lib/IMPL/DOM/Schema.pm @ 385:0d63f5273307
sync
author | sergey |
---|---|
date | Tue, 11 Feb 2014 01:05:55 +0400 |
parents | 2f16f13b000c |
children | 5aff94ba842f |
line wrap: on
line source
package IMPL::DOM::Schema; use strict; use warnings; use File::Spec; use IMPL::Const qw(:prop); use IMPL::declare { require => { ComplexNode => 'IMPL::DOM::Schema::ComplexNode', ComplexType => 'IMPL::DOM::Schema::ComplexType', SimpleNode => 'IMPL::DOM::Schema::SimpleNode', SimpleType => 'IMPL::DOM::Schema::SimpleType', Node => 'IMPL::DOM::Schema::Node', AnyNode => 'IMPL::DOM::Schema::AnyNode', NodeList => 'IMPL::DOM::Schema::NodeList', NodeSet => 'IMPL::DOM::Schema::NodeSet', Property => 'IMPL::DOM::Schema::Property', SwitchNode => 'IMPL::DOM::Schema::SwitchNode', Validator => 'IMPL::DOM::Schema::Validator', Builder => 'IMPL::DOM::Navigator::Builder', XMLReader => 'IMPL::DOM::XMLReader', # XMLReader references Schema InflateFactory => 'IMPL::DOM::Schema::InflateFactory', Loader => 'IMPL::Code::Loader', StringMap => 'IMPL::Resources::StringLocaleMap' }, base => [ 'IMPL::DOM::Document' => sub { nodeName => 'schema' } ], props => [ _typesMap => PROP_RW | PROP_DIRECT, baseDir => PROP_RW | PROP_DIRECT, schemaName => PROP_RW | PROP_DIRECT, baseSchemas => PROP_RO | PROP_LIST | PROP_DIRECT, stringMap => { get => '_getStringMap', direct => 1 } ] }; my $validatorLoader = Loader->new(prefix => Validator, verifyNames => 1); #TODO rename and remove sub resolveType { goto &ResolveType; } sub CTOR { my ($this,%args) = @_; $this->{$baseDir} = ($args{baseDir} || '.'); } # compat sub ResolveType { my ($this,$typeName) = @_; my $type = $this->{$_typesMap}{$typeName}; return $type if $type; foreach my $base ($this->baseSchemas) { last if $type = $base->resolveType($typeName); } die IMPL::KeyNotFoundException->new($typeName) unless $type; return $this->{$_typesMap}{$typeName} = $type; } sub Create { my ($this,$nodeName,$class,$refArgs) = @_; die new IMPL::Exception('Invalid node class') unless $class->isa('IMPL::DOM::Node'); if ($class->isa('IMPL::DOM::Schema::Validator')) { $class = $validatorLoader->GetFullName($nodeName); unless (eval {$class->can('new')}) { eval { $validatorLoader->Require($nodeName); }; my $e = $@; die new IMPL::Exception("Invalid validator",$class,$e) if $e; } } return $this->SUPER::Create($nodeName,$class,$refArgs); } sub _getStringMap { my ($this) = @_; return $this->{$stringMap} if $this->{$stringMap}; my $map = StringMap->new(); if ($this->baseDir and $this->schemaName) { $map->paths( File::Spec->catdir($this->baseDir,'locale') ); $map->name( $this->schemaName ); } return $this->{$stringMap} = $map; } sub Process { my ($this) = @_; # process instructions $this->Include($_) foreach map $_->nodeProperty('source'), $this->selectNodes('Include'); # build types map $this->{$_typesMap} = { map { $_->type, $_ } $this->selectNodes(sub { $_[0]->nodeName eq 'ComplexType' || $_[0]->nodeName eq 'SimpleType' } ) }; } sub Include { my ($this,$file) = @_; my $schema = $this->LoadSchema(File::Spec->catfile($this->baseDir, $file)); $this->baseSchemas->Append( $schema ); } sub LoadSchema { my ($this,$file) = @_; $file = File::Spec->rel2abs($file); my $class = ref $this || $this; my $reader = XMLReader->new( Navigator => Builder->new( $class, $class->MetaSchema ), SkipWhitespace => 1 ); $reader->ParseFile($file); my $schema = $reader->Navigator->Document; my ($vol,$dir,$name) = File::Spec->splitpath($file); $name =~ s/\.xml$//; $schema->baseDir($dir); $schema->schemaName($name); my @errors = $class->MetaSchema->Validate($schema); die new IMPL::Exception("Schema is invalid",$file,map( $_->message, @errors ) ) if @errors; $schema->Process; return $schema; } sub Validate { my ($this,$node) = @_; if ( my ($schemaNode) = $this->selectNodes(sub { $_->isa(Node) and $_[0]->name eq $node->nodeName })) { $schemaNode->Validate($node); } else { return new IMPL::DOM::Schema::ValidationError(node => $node, message=> "A specified document (%Node.nodeName%) doesn't match the schema"); } } my $schema; sub MetaSchema { return $schema if $schema; $schema = __PACKAGE__->new(); $schema->appendRange( ComplexNode->new(name => 'schema')->appendRange( NodeSet->new()->appendRange( Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'ComplexType', type => 'ComplexType', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SimpleType', type => 'SimpleType', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'Node', type => 'Node', minOccur => 0, maxOccur=>'unbounded'), SimpleNode->new(name => 'Include', minOccur => 0, maxOccur=>'unbounded')->appendRange( Property->new(name => 'source') ) ), ), ComplexType->new(type => 'NodeSet', nativeType => 'IMPL::DOM::Schema::NodeSet')->appendRange( NodeSet->new()->appendRange( Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'Node', type=>'Node', minOccur => 0, maxOccur=>'unbounded'), SwitchNode->new(minOccur => 0, maxOccur => 1)->appendRange( Node->new(name => 'AnyNode', type => 'AnyNode'), Node->new(name => 'SwitchNode',type => 'SwitchNode') ) ) ), ComplexType->new(type => 'SwitchNode', nativeType => 'IMPL::DOM::Schema::SwitchNode')->appendRange( NodeSet->new()->appendRange( Node->new(name => 'ComplexNode', type=>'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SimpleNode', type=>'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'Node', type=>'Node', minOccur => 0, maxOccur=>'unbounded'), ) ), ComplexType->new(type => 'NodeList', nativeType => 'IMPL::DOM::Schema::NodeList')->appendRange( NodeSet->new()->appendRange( Node->new(name => 'ComplexNode', type => 'ComplexNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SimpleNode', type => 'SimpleNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'SwitchNode',type => 'SwitchNode', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'Node', type => 'Node', minOccur => 0, maxOccur=>'unbounded'), Node->new(name => 'AnyNode', type => 'AnyNode', minOccur => 0, maxOccur=>'unbounded'), ) ), ComplexType->new(type => 'ComplexType', nativeType => 'IMPL::DOM::Schema::ComplexType')->appendRange( NodeList->new()->appendRange( SwitchNode->new()->appendRange( Node->new(name => 'NodeSet', type => 'NodeSet'), Node->new(name => 'NodeList',type => 'NodeList'), ), Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') ), Property->new(name => 'type') ), ComplexType->new(type => 'ComplexNode', nativeType => 'IMPL::DOM::Schema::ComplexNode')->appendRange( NodeList->new()->appendRange( SwitchNode->new()->appendRange( Node->new(name => 'NodeSet', type => 'NodeSet'), Node->new(name => 'NodeList',type => 'NodeList'), ), Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') ), Property->new(name => 'name') ), ComplexType->new(type => 'SimpleType', nativeType => 'IMPL::DOM::Schema::SimpleType')->appendRange( NodeList->new()->appendRange( Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') ), Property->new(name => 'type'), Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') ), ComplexType->new(type => 'SimpleNode', nativeType => 'IMPL::DOM::Schema::SimpleNode')->appendRange( NodeList->new()->appendRange( Node->new(name => 'Property', type=>'Property', maxOccur=>'unbounded', minOccur=>0), AnyNode->new(maxOccur => 'unbounded', minOccur => 0, type=>'Validator') ), Property->new(name => 'name'), Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') ), ComplexType->new(type => 'Validator', nativeType => 'IMPL::DOM::Schema::Validator')->appendRange( NodeList->new()->appendRange( AnyNode->new(maxOccur => 'unbounded', minOccur => 0) ) ), ComplexType->new(type => 'Property', nativeType => 'IMPL::DOM::Schema::Property' )->appendRange( NodeList->new()->appendRange( AnyNode->new(maxOccur => 'unbounded', minOccur => 0) ), Property->new(name => 'name'), Property->new(name => 'inflator', optional => 1, inflator => 'IMPL::DOM::Schema::InflateFactory') ), SimpleType->new(type => 'Node', nativeType => 'IMPL::DOM::Schema::Node')->appendRange( Property->new(name => 'name'), Property->new(name => 'type') ), SimpleType->new(type => 'AnyNode', nativeType => 'IMPL::DOM::Schema::AnyNode') ); $schema->Process; return $schema; } 1; __END__ =pod =head1 NAME C<IMPL::DOM::Schema> - Схема документа. =head1 DESCRIPTION C<use parent qw(IMPL::DOM::Document)> DOM схема - это документ, состоящий из определенных узлов, описывающая структуру других документов. =head1 METHODS =over =item C<< $obj->Process() >> Обновляет таблицу типов из содержимого. =item C<< $obj->ResolveType($typeName) >> Возвращает схему типа c именем C<$typeName>. =back =head1 META SCHEMA Схема для описания схемы, эта схема используется для постороения других схем, выглядит приблизительно так =begin code xml <schema> <ComplexNode name="schema"> <NodeSet> <Node minOcuur="0" maxOccur="unbounded" name="ComplexNode" type="ComplexNode"/> <Node minOcuur="0" maxOccur="unbounded" name="SimpleNode" type="SimpleNode"/> <Node minOcuur="0" maxOccur="unbounded" name="ComplexType" type="ComplexType"/> <Node minOcuur="0" maxOccur="unbounded" name="SimpleType" type="SimpleType"/> <SimpleNode minOcuur="0" maxOccur="unbounded" name="Node"/> <SimpleNode minOcuur="0" maxOccur="unbounded" name="Include"/> </NodeSet> </ComplexNode> <ComplexType type="NodeContainer"> <NodeSet> <Node minOcuur="0" maxOccur="unbounded" name="ComplexNode" type="ComplexNode"/> <Node minOcuur="0" maxOccur="unbounded" name="SimpleNode" type="SimpleNode"/> <SimpleNode minOcuur="0" maxOccur="unbounded" name="Node"/> </NodeSet> </ComplexType> <ComplexType type="ComplexType"> <NodeList> <Node name="NodeSet" type="NodeContainer" minOcuur=0/> <Node name="NodeList" type="NodeContainer" minOccur=0/> <AnyNode minOccur="0" maxOccur="unbounded" type="Validator"/> </NodeList> </ComplexType> <ComplexType type="ComplexNode"> <NodeList> <Node name="NodeSet" type="NodeContainer" minOcuur=0/> <Node name="NodeList" type="NodeContainer" minOccur=0/> <AnyNode minOccur="0" maxOccur="unbounded" type="Validator"/> </NodeList> </ComplexType> <ComplexType type="SimpleNode"> <NodeSet> <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/> </NodeSet> </ComplexType> <ComplexType type="SimpleType"> <NodeSet> <AnyNode minOccur=0 maxOccur="unbounded" type="Validator"/> </NodeSet> </ComplexType> <ComplexType type="Validator"> <NodeSet> <AnyNode minOccur=0 maxOccur="unbounded"/> </NodeSet> </ComplexType> </schema> =end code xml =cut