Mercurial > pub > Impl
view Lib/IMPL/DOM/Schema/Validator/Compare.pm @ 322:cca158327c47
added OutOfRangeException
author | cin |
---|---|
date | Tue, 21 May 2013 10:04:11 +0400 |
parents | 89179bb8c388 |
children | 5aff94ba842f |
line wrap: on
line source
package IMPL::DOM::Schema::Validator::Compare; use strict; use parent qw(IMPL::DOM::Schema::Validator); use IMPL::Resources::Format qw(FormatMessage); use IMPL::Class::Property; BEGIN { public property targetProperty => prop_all; public property op => prop_all; public property nodePath => prop_all; public property optional => prop_all; private property _pathTranslated => prop_all; private property _targetNode => prop_all; private property _sourceSchema => prop_all; public property message => prop_all; } our %CTOR = ( 'IMPL::DOM::Schema::Validator' => sub { my %args = @_; $args{nodeName} ||= 'Compare'; delete @args{qw(targetProperty op nodePath optional message)}; %args; } ); our %Ops = ( '=' => \&_equals, 'eq' => \&_equalsString, '!=' => \&_notEquals, 'ne' => \&_notEqualsString, '=~' => \&_matchRx, '!~' => \&_notMatchRx, '<' => \&_less, '>' => \&_greater, 'lt' => \&_lessString, 'gt' => \&_greaterString ); my $rxOps = map qr/$_/, join( '|', keys %Ops ); sub CTOR { my ($this,%args) = @_; $this->targetProperty($args{targetProperty} || 'nodeValue'); $this->op( $Ops{ $args{op} || '=' } ) or die new IMPL::InvalidArgumentException("Invalid parameter value",'op',$args{op},$this->path); $this->nodePath($args{nodePath}) or die new IMPL::InvalidArgumentException("The argument is required", 'nodePath', $this->path); $this->message($args{message} || 'The value of %node.path% %source.op% %value% (%source.nodePath%)' ); $this->optional($args{optional}) if $args{optional}; } sub TranslatePath { my ($this,$path) = @_; $path ||= ''; my @selectQuery; my $i = 0; foreach my $chunk (split /\//,$path) { $chunk = 'document:*' if $i == 0 and not length $chunk; next if not length $chunk; my $query; my ($axis,$filter) = ( $chunk =~ /^(?:(\w+):)?(.*)$/); if ($filter =~ /^\w+|\*$/ ) { $query = $filter eq '*' ? undef : $filter; } elsif ( $filter =~ /^(\w+|\*)\s*((?:\[\s*\w+\s*(?:=|!=|=~|!~|eq|ne|lt|gt)\s*["'](?:[^\\'"]|\\[\\"'])*["']\])+)$/) { my ($nodeName,$filterArgs) = ($1,$2); my @parsedFilters = map { my ($prop,$op,$value) = ($_ =~ /\s*(\w+)\s*(=|!=|=~|!~|eq|ne|lt|gt)\s*(?:["']((?:[^\\'"]|\\[\\"'])*)["'])/); $value =~ s/\\[\\'"]/$1/g; { prop => $prop, op => $Ops{$op}, value => $value } } grep ( $_, split ( /[\]\[]+/,$filterArgs ) ); $query = sub { my ($node) = shift; $node->nodeName eq $nodeName or return 0 if $nodeName ne '*'; $_->{op}->( _resovleProperty($node,$_->{prop}), FormatMessage($_->{value},{ Schema => $this->parentNode, Node => $this->_targetNode, schema => $this->parentNode, node => $this->_targetNode, source => $this->_sourceSchema },\&_resovleProperty) ) or return 0 foreach @parsedFilters; return 1; }; } else { die new IMPL::Exception("Invalid query syntax",$path,$chunk); } push @selectQuery, $axis ? { $axis => $query } : $query; $i++; } return \@selectQuery; } sub Validate { my ($this,$node,$ctx) = @_; my @result; my $Source = $ctx && $ctx->{Source} || $this->parentNode; $this->_sourceSchema($Source); $this->_targetNode($node); my $query = $this->_pathTranslated() || $this->_pathTranslated($this->TranslatePath($this->nodePath)); my ($foreignNode) = $node->selectNodes(@$query); if ($foreignNode) { my $value = $this->nodeValue; if ($value) { $value = FormatMessage($value, { Schema => $this->parentNode, Node => $this->_targetNode, ForeignNode => $foreignNode },\&_resovleProperty); } else { $value = $foreignNode->nodeValue; } push @result, new IMPL::DOM::Schema::ValidationError( node => $node, foreignNode => $foreignNode, value => $value, source => $Source, schema => $this->parentNode, message => $this->message ) unless $this->op->(_resovleProperty($node,$this->targetProperty),$value); } elsif (not $this->optional) { push @result, new IMPL::DOM::Schema::ValidationError( node => $node, value => '', source => $Source, schema => $this->parentNode, message => $this->message ); } $this->_targetNode(undef); $this->_sourceSchema(undef); return @result; } sub _resovleProperty { my ($node,$prop) = @_; return $node->can($prop) ? $node->$prop() : $node->nodeProperty($prop); } sub _matchRx { $_[0] =~ $_[1]; } sub _notMatchRx { $_[0] !~ $_[1]; } sub _equals { $_[0] == $_[1]; } sub _notEquals { $_[0] != $_[0]; } sub _equalsString { $_[0] eq $_[1]; } sub _notEqualsString { $_[0] ne $_[1]; } sub _less { $_[0] < $_[1]; } sub _greater { $_[0] > $_[1]; } sub _lessString { $_[0] lt $_[1]; } sub _greaterString { $_[0] gt $_[1]; } sub _lessEq { $_[0] <= $_[1]; } sub _greaterEq { $_[0] >= $_[1]; } 1; __END__ =pod =head1 NAME C<IMPL::DOM::Schema::Validator::Compare> - ограничение на содержимое текущего узла, сравнивая его со значением другого узла. =head1 SYNOPSIS Пример типа описания поля с проверочным полем =begin code xml <schema> <SimpleType type="retype_field"> <Property name="linkedNode" message="Для узла %Node.nodeName% необходимо задать свойство %Source.name%"/> <Compare op="eq" nodePath="sibling:*[nodeName eq '%Node.linkedNode%']"/> </SimpleType> </schema> =begin code xml =head1 DESCRIPTION Позволяет сравнивать значение текущего узла со значением другого узла. =cut