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

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/IMPL/DOM/Schema.pm	Fri Sep 04 19:40:23 2015 +0300
@@ -0,0 +1,371 @@
+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
+	    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->Push( $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')
+        ),
+        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')
+        ),
+        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')
+        ),
+        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