changeset 103:c289ed9662ca

Schema beta 2 More strict validation, support for inflating a simple nodes and properties
author wizard
date Fri, 07 May 2010 18:17:40 +0400
parents cf3b6ef2be22
children 196bf443b5e1
files Lib/IMPL/DOM/Navigator/Builder.pm Lib/IMPL/DOM/Navigator/SchemaNavigator.pm Lib/IMPL/DOM/Navigator/SimpleBuilder.pm Lib/IMPL/DOM/Schema.pm Lib/IMPL/DOM/Schema/ComplexType.pm Lib/IMPL/DOM/Schema/Node.pm Lib/IMPL/DOM/Schema/Property.pm Lib/IMPL/DOM/Schema/SimpleNode.pm Lib/IMPL/DOM/Schema/SimpleType.pm Lib/IMPL/DOM/XMLReader.pm _test/Resources/types.xml _test/any.pl
diffstat 12 files changed, 139 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/DOM/Navigator/Builder.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Navigator/Builder.pm	Fri May 07 18:17:40 2010 +0400
@@ -11,7 +11,7 @@
     private _direct property _schemaNavi => prop_all;
     private _direct property _nodesPath => prop_all;
     private _direct property _nodeCurrent => prop_all;
-    private _direct property _docClass => prop_all
+    private _direct property _docClass => prop_all;
     public _direct property Document => prop_get | owner_set;
 }
 
@@ -27,6 +27,7 @@
     
     if (my $schemaNode = $this->{$_schemaNavi}->NavigateName($nodeName)) {
         my $class = $schemaNode->can('nativeType') ? $schemaNode->nativeType : 'IMPL::DOM::Node';
+        $this->inflateProperties($schemaNode,\%props);
         
         my $node;
         if (! $this->{$Document}) {
@@ -42,11 +43,31 @@
         
         return $node;
     } else {
-    	warn $nodeName;
         die new IMPL::InvalidOperationException("The specified node is undefined", $nodeName);
     }
 }
 
+sub inflateProperties {
+	my ($this,$schemaNode,$refProps) = @_;
+	
+	$refProps->{$_->name} = $_->inflator->new($refProps->{$_->name})
+		foreach $schemaNode->selectNodes(
+			sub {
+				$_->nodeName eq 'Property' and exists $refProps->{$_->name} and $_->inflator 
+			}
+		);
+}
+
+sub inflateValue {
+	my ($this,$value) = @_;
+	my $schemaNode = $this->{$_schemaNavi}->Current;
+	if ($schemaNode->can('inflator') and my $inflator = $schemaNode->inflator) {
+		return $inflator->new($value);
+	} else {
+		return $value;
+	}
+}
+
 sub Back {
     my ($this) = @_;
     
--- a/Lib/IMPL/DOM/Navigator/SchemaNavigator.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Navigator/SchemaNavigator.pm	Fri May 07 18:17:40 2010 +0400
@@ -110,8 +110,6 @@
 =item C<< $navi->NavigateName($name) >>
 
 Переходит на схему узла с указанным именем. Тоесть использует свойство C<name>.
-В данном случае всегда происходит безопасная навигация, тоесть в случае неудачи,
-навигатор останется на прежней позиции.
 
 =item C<< $navi->SchemaBack >>
 
--- a/Lib/IMPL/DOM/Navigator/SimpleBuilder.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Navigator/SimpleBuilder.pm	Fri May 07 18:17:40 2010 +0400
@@ -34,4 +34,8 @@
     return $node;
 }
 
+sub inflateValue {
+	$_[1];
+}
+
 1;
--- a/Lib/IMPL/DOM/Schema.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema.pm	Fri May 07 18:17:40 2010 +0400
@@ -29,7 +29,6 @@
     private _direct property _TypesMap => prop_all;
     public _direct property baseDir => prop_all;
     public _direct property BaseSchemas => prop_get | owner_set;
-    private _direct property _Validators => prop_all;
 }
 
 sub resolveType {
--- a/Lib/IMPL/DOM/Schema/ComplexType.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/ComplexType.pm	Fri May 07 18:17:40 2010 +0400
@@ -8,6 +8,7 @@
 
 BEGIN {
     public _direct property nativeType => prop_get;
+    public _direct property messageWrongType => prop_get;
 }
 
 our %CTOR = (
@@ -25,10 +26,25 @@
     my ($this,%args) = @_;
     
     $this->{$nativeType} = $args{nativeType};
+    $this->{$messageWrongType} = $args{messageWrongType} || "A complex node '%Node.path%' is expected to be %Schema.nativeType%";
+}
+
+sub Validate {
+	my ($this, $node) = @_;
+	
+	if ($this->{$nativeType}) {
+		return new IMPL::DOM::Schema::ValidationError(
+			Node => $node,
+			Source => $this,
+			Schema => $this,
+			Message => $this->messageWrongType
+		) unless $node->isa($this->{$nativeType});
+	}
+	return $this->SUPER::Validate($node);
 }
 
 sub qname {
-	$_[0]->nodeName.'[name='.$_[0]->type.']';
+	$_[0]->nodeName.'[type='.$_[0]->type.']';
 }
 
 
--- a/Lib/IMPL/DOM/Schema/Node.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/Node.pm	Fri May 07 18:17:40 2010 +0400
@@ -43,6 +43,8 @@
     }
 }
 
+sub inflator { undef; }
+
 sub qname {
     $_[0]->nodeName.'[name='.$_[0]->{$name}.']';
 }
--- a/Lib/IMPL/DOM/Schema/Property.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/Property.pm	Fri May 07 18:17:40 2010 +0400
@@ -34,21 +34,23 @@
 sub Validate {
     my ($this,$node) = @_;
     
-    if ($this->minOccur) {
-        my $prop = $this->name;
-        my $nodeProp = new IMPL::DOM::Node(nodeName => '::property', nodeValue => eval { $node->$prop() } || $node->nodeProperty($prop));
+    my $prop = $this->name;
+    
+    # buld a pseudo node for the property value 
+    my $nodeProp = new IMPL::DOM::Node(nodeName => '::property', nodeValue => eval { $node->$prop() } || $node->nodeProperty($prop));
         
-        if (! $nodeProp->nodeValue) {
-            return new IMPL::DOM::Schema::ValidationError(
-                Message => $this->RequiredMessage,
-                Node => $node,
-                Schema => $this
-            );
-        }
-        return $this->SUPER::Validate($nodeProp);
-    } else {
-        return ();
+    if ($nodeProp->nodeValue) {
+    	# we have a value so validate it
+    	return $this->SUPER::Validate($nodeProp);
+    } elsif($this->minOccur) {
+    	# we don't have a value but it's a mandatory property
+        return new IMPL::DOM::Schema::ValidationError(
+            Message => $this->RequiredMessage,
+            Node => $node,
+            Schema => $this
+        );
     }
+    
 }
 
 1;
--- a/Lib/IMPL/DOM/Schema/SimpleNode.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/SimpleNode.pm	Fri May 07 18:17:40 2010 +0400
@@ -3,15 +3,27 @@
 use warnings;
 
 use base qw(IMPL::DOM::Schema::Node);
+use IMPL::Class::Property;
+use IMPL::Class::Property::Direct;
+
+BEGIN {
+	public _direct property inflator => prop_get;
+}
 
 our %CTOR = (
     'IMPL::DOM::Schema::Node' => sub {my %args = @_; $args{nodeName} ||= 'SimpleNode'; %args}
-);  
+);
+
+sub CTOR {
+	my ($this,%args) = @_;
+	
+	$this->{$inflator} = $args{inflator} if $args{iflator};
+}
 
 sub Validate {
     my ($this,$node) = @_;
     
-    map $_->Validate($node), @{$this->childNodes};    
+    return map $_->Validate($node), @{$this->childNodes};
 }
 
 1;
@@ -20,10 +32,18 @@
 
 =pod
 
+=head1 NAME
+
+C<IMPL::DOM::SimpleNode> - узел с текстом.
+
 =head1 DESCRIPTION
 
 Узел имеющий простое значение. Данный узел может содержать ограничения
 на простое значение.
 
+Производит валидацию содержимого, при постоении DOM модели не имеет специального
+типа и будет создан в виде C<IMPL::DOM::Node>.
+
+Также определяет как будет воссоздано значение узла в DOM модели.
 
 =cut
--- a/Lib/IMPL/DOM/Schema/SimpleType.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/Schema/SimpleType.pm	Fri May 07 18:17:40 2010 +0400
@@ -8,6 +8,7 @@
 
 BEGIN {
     public _direct property nativeType => prop_get;
+    public _direct property messageWrongType => prop_get;
 }
 
 our %CTOR = (
@@ -25,11 +26,55 @@
     my ($this,%args) = @_;
     
     $this->{$nativeType} = $args{nativeType};
+    $this->{$messageWrongType} = $args{messageWrongType} || "A simple node '%Node.path%' is expected to be %Schema.nativeType%"; 
+}
+
+sub Validate {
+	my ($this, $node) = @_;
+	
+	if ($this->{$nativeType}) {
+		return new IMPL::DOM::Schema::ValidationError(
+			Node => $node,
+			Source => $this,
+			Schema => $this,
+			Message => $this->messageWrongType
+		) unless $node->isa($this->{$nativeType});
+	}
+	return $this->SUPER::Validate($node);
 }
 
 sub qname {
-	$_[0]->nodeName.'[name='.$_[0]->type.']';
+	$_[0]->nodeName.'[type='.$_[0]->type.']';
 }
 
+1;
 
-1;
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::DOM::Schema::SimpleType> - тип для простых узлов.
+
+=head1 DESCRIPTION
+
+Используется для описания простых узлов, которые можно отобразить в узлы
+определенного типа при построении DOM документа.
+
+=head1 MEMBERS
+
+=over
+
+=item C<nativeType>
+
+Имя класса который будет представлять узел в DOM модели.
+
+=item C<messageWrongType>
+
+Формат сообщения которое будет выдано, если узел в дом модели не будет
+соответствовать свойству C<nativeType>.
+
+=back
+
+=cut
--- a/Lib/IMPL/DOM/XMLReader.pm	Fri May 07 08:05:23 2010 +0400
+++ b/Lib/IMPL/DOM/XMLReader.pm	Fri May 07 18:17:40 2010 +0400
@@ -56,7 +56,7 @@
 sub _OnEnd {
     my ($this,$element) = @_;
     
-    $this->{$_current}->nodeValue($this->{$_text}) if length $this->{$_text};
+    $this->{$_current}->nodeValue($this->Navigator->inflateValue( $this->{$_text} ) ) if length $this->{$_text};
     $this->{$_text} = pop @{$this->{$_textHistory}};
     $this->{$_current} = $this->Navigator->Back;
 }
--- a/_test/Resources/types.xml	Fri May 07 08:05:23 2010 +0400
+++ b/_test/Resources/types.xml	Fri May 07 18:17:40 2010 +0400
@@ -1,10 +1,9 @@
 <schema>
-	<SimpleType type="email" nativeType="SCALAR">
+	<SimpleType type="email">
 		<RegExp message="Неверный формат %Node.name_no%">^\w+(\.\w+)*@$\w+(\.\w+)+</RegExp>
-		<Property name='locale'/>
 	</SimpleType>
-	<SimpleType type="scalar" nativeType="SCALAR"/>
-	<SimpleType type="date" nativeType="DateTime">
+	<SimpleType type="scalar"/>
+	<SimpleType type="date" inflate="DateTime">
 		<Property name="timezone" optional="1"/>
 		<Property name="locale" optional="1"/>
 	</SimpleType>
--- a/_test/any.pl	Fri May 07 08:05:23 2010 +0400
+++ b/_test/any.pl	Fri May 07 18:17:40 2010 +0400
@@ -4,6 +4,7 @@
 
 require IMPL::DOM::Navigator::SimpleBuilder;
 require IMPL::DOM::XMLReader;
+require IMPL::DOM::Schema;
 
 my $builder = IMPL::DOM::Navigator::SimpleBuilder->new();
     
@@ -59,6 +60,10 @@
     $reader2->ParseFile("Resources/person_info.xml");
     print "Parsing small Xml file: ",tv_interval($t,[gettimeofday]),"\n";
     
+    $t = [gettimeofday];
+    IMPL::DOM::Schema->LoadSchema('Resources/form.xml') for 1..10;
+    print "Load a small schema 10 times: ",tv_interval($t,[gettimeofday]),"\n";
+    
 sub selectAll {
     my $node = shift;
     $node,map selectAll($_),@{$node->childNodes};