Mercurial > pub > Impl
comparison Lib/IMPL/DOM/Schema/Validator/Compare.pm @ 194:4d0e1962161c
Replaced tabs with spaces
IMPL::Web::View - fixed document model, new features (control classes, document constructor parameters)
| author | cin |
|---|---|
| date | Tue, 10 Apr 2012 20:08:29 +0400 |
| parents | d1676be8afcc |
| children | 2904da230022 |
comparison
equal
deleted
inserted
replaced
| 193:8e8401c0aea4 | 194:4d0e1962161c |
|---|---|
| 5 | 5 |
| 6 use IMPL::Resources::Format qw(FormatMessage); | 6 use IMPL::Resources::Format qw(FormatMessage); |
| 7 use IMPL::Class::Property; | 7 use IMPL::Class::Property; |
| 8 | 8 |
| 9 BEGIN { | 9 BEGIN { |
| 10 public property targetProperty => prop_all; | 10 public property targetProperty => prop_all; |
| 11 public property op => prop_all; | 11 public property op => prop_all; |
| 12 public property nodePath => prop_all; | 12 public property nodePath => prop_all; |
| 13 public property optional => prop_all; | 13 public property optional => prop_all; |
| 14 private property _pathTranslated => prop_all; | 14 private property _pathTranslated => prop_all; |
| 15 private property _targetNode => prop_all; | 15 private property _targetNode => prop_all; |
| 16 public property message => prop_all; | 16 public property message => prop_all; |
| 17 } | 17 } |
| 18 | 18 |
| 19 our %CTOR = ( | 19 our %CTOR = ( |
| 20 'IMPL::DOM::Schema::Validator' => sub { | 20 'IMPL::DOM::Schema::Validator' => sub { |
| 21 my %args = @_; | 21 my %args = @_; |
| 22 $args{nodeName} ||= 'Compare'; | 22 $args{nodeName} ||= 'Compare'; |
| 23 delete @args{qw(targetProperty op nodePath optional message)}; | 23 delete @args{qw(targetProperty op nodePath optional message)}; |
| 24 %args; | 24 %args; |
| 25 } | 25 } |
| 26 ); | 26 ); |
| 27 | 27 |
| 28 our %Ops = ( | 28 our %Ops = ( |
| 29 '=' => \&_equals, | 29 '=' => \&_equals, |
| 30 'eq' => \&_equalsString, | 30 'eq' => \&_equalsString, |
| 31 '!=' => \&_notEquals, | 31 '!=' => \&_notEquals, |
| 32 'ne' => \&_notEqualsString, | 32 'ne' => \&_notEqualsString, |
| 33 '=~' => \&_matchRx, | 33 '=~' => \&_matchRx, |
| 34 '!~' => \&_notMatchRx, | 34 '!~' => \&_notMatchRx, |
| 35 '<' => \&_less, | 35 '<' => \&_less, |
| 36 '>' => \&_greater, | 36 '>' => \&_greater, |
| 37 'lt' => \&_lessString, | 37 'lt' => \&_lessString, |
| 38 'gt' => \&_greaterString | 38 'gt' => \&_greaterString |
| 39 ); | 39 ); |
| 40 | 40 |
| 41 my $rxOps = map qr/$_/, join( '|', keys %Ops ); | 41 my $rxOps = map qr/$_/, join( '|', keys %Ops ); |
| 42 | 42 |
| 43 sub CTOR { | 43 sub CTOR { |
| 44 my ($this,%args) = @_; | 44 my ($this,%args) = @_; |
| 45 | 45 |
| 46 $this->targetProperty($args{targetProperty} || 'nodeValue'); | 46 $this->targetProperty($args{targetProperty} || 'nodeValue'); |
| 47 $this->op( $Ops{ $args{op} || '=' } ) or die new IMPL::InvalidArgumentException("Invalid parameter value",'op',$args{op},$this->path); | 47 $this->op( $Ops{ $args{op} || '=' } ) or die new IMPL::InvalidArgumentException("Invalid parameter value",'op',$args{op},$this->path); |
| 48 $this->nodePath($args{nodePath}) or die new IMPL::InvalidArgumentException("The argument is required", 'nodePath', $this->path); | 48 $this->nodePath($args{nodePath}) or die new IMPL::InvalidArgumentException("The argument is required", 'nodePath', $this->path); |
| 49 $this->message($args{message} || 'The value of %Node.path% %Source.op% %Value% (%Source.nodePath%)' ); | 49 $this->message($args{message} || 'The value of %Node.path% %Source.op% %Value% (%Source.nodePath%)' ); |
| 50 $this->optional($args{optional}) if $args{optional}; | 50 $this->optional($args{optional}) if $args{optional}; |
| 51 } | 51 } |
| 52 | 52 |
| 53 sub TranslatePath { | 53 sub TranslatePath { |
| 54 my ($this,$path) = @_; | 54 my ($this,$path) = @_; |
| 55 | 55 |
| 56 $path ||= ''; | 56 $path ||= ''; |
| 57 | 57 |
| 58 my @selectQuery; | 58 my @selectQuery; |
| 59 | 59 |
| 60 my $i = 0; | 60 my $i = 0; |
| 61 | 61 |
| 62 foreach my $chunk (split /\//,$path) { | 62 foreach my $chunk (split /\//,$path) { |
| 63 $chunk = 'document:*' if $i == 0 and not length $chunk; | 63 $chunk = 'document:*' if $i == 0 and not length $chunk; |
| 64 next if not length $chunk; | 64 next if not length $chunk; |
| 65 | 65 |
| 66 my $query; | 66 my $query; |
| 67 my ($axis,$filter) = ( $chunk =~ /^(?:(\w+):)?(.*)$/); | 67 my ($axis,$filter) = ( $chunk =~ /^(?:(\w+):)?(.*)$/); |
| 68 | 68 |
| 69 if ($filter =~ /^\w+|\*$/ ) { | 69 if ($filter =~ /^\w+|\*$/ ) { |
| 70 $query = $filter eq '*' ? undef : $filter; | 70 $query = $filter eq '*' ? undef : $filter; |
| 71 } elsif ( $filter =~ /^(\w+|\*)\s*((?:\[\s*\w+\s*(?:=|!=|=~|!~|eq|ne|lt|gt|)\s*["'](?:[\\'"]|\\[\\"'])*["']\])+)$/) { | 71 } elsif ( $filter =~ /^(\w+|\*)\s*((?:\[\s*\w+\s*(?:=|!=|=~|!~|eq|ne|lt|gt|)\s*["'](?:[\\'"]|\\[\\"'])*["']\])+)$/) { |
| 72 my ($nodeName,$filterArgs) = ($1,$2); | 72 my ($nodeName,$filterArgs) = ($1,$2); |
| 73 | 73 |
| 74 my @parsedFilters = map { | 74 my @parsedFilters = map { |
| 75 my ($prop,$op,$value) = ($_ =~ /\s*(\w+)\s*(=|!=|=~|!~)\s*(["'](?:[\\'"]|\\[\\"'])*["'])/); | 75 my ($prop,$op,$value) = ($_ =~ /\s*(\w+)\s*(=|!=|=~|!~)\s*(["'](?:[\\'"]|\\[\\"'])*["'])/); |
| 76 $value =~ s/\\[\\'"]/$1/g; | 76 $value =~ s/\\[\\'"]/$1/g; |
| 77 { | 77 { |
| 78 prop => $prop, | 78 prop => $prop, |
| 79 op => $Ops{$op}, | 79 op => $Ops{$op}, |
| 80 value => $value | 80 value => $value |
| 81 } | 81 } |
| 82 } grep ( $_, split ( /[\]\[]+/,$filterArgs ) ); | 82 } grep ( $_, split ( /[\]\[]+/,$filterArgs ) ); |
| 83 | 83 |
| 84 $query = sub { | 84 $query = sub { |
| 85 my ($node) = shift; | 85 my ($node) = shift; |
| 86 | 86 |
| 87 $node->nodeName eq $nodeName or return 0 if $nodeName ne '*'; | 87 $node->nodeName eq $nodeName or return 0 if $nodeName ne '*'; |
| 88 $_->{op}->( | 88 $_->{op}->( |
| 89 _resovleProperty($node,$_->{prop}), | 89 _resovleProperty($node,$_->{prop}), |
| 90 FormatMessage($_->{value},{ | 90 FormatMessage($_->{value},{ |
| 91 Schema => $this->parentNode, | 91 Schema => $this->parentNode, |
| 92 Node => $this->_targetNode | 92 Node => $this->_targetNode |
| 93 },\&_resovleProperty) | 93 },\&_resovleProperty) |
| 94 ) or return 0 foreach @parsedFilters; | 94 ) or return 0 foreach @parsedFilters; |
| 95 | 95 |
| 96 }; | 96 }; |
| 97 } else { | 97 } else { |
| 98 die new IMPL::Exception("Invalid query syntax",$path,$chunk); | 98 die new IMPL::Exception("Invalid query syntax",$path,$chunk); |
| 99 } | 99 } |
| 100 | 100 |
| 101 push @selectQuery, $axis ? { $axis => $query } : $query; | 101 push @selectQuery, $axis ? { $axis => $query } : $query; |
| 102 | 102 |
| 103 $i++; | 103 $i++; |
| 104 } | 104 } |
| 105 | 105 |
| 106 return \@selectQuery; | 106 return \@selectQuery; |
| 107 } | 107 } |
| 108 | 108 |
| 109 sub Validate { | 109 sub Validate { |
| 110 my ($this,$node,$ctx) = @_; | 110 my ($this,$node,$ctx) = @_; |
| 111 | 111 |
| 112 my @result; | 112 my @result; |
| 113 | 113 |
| 114 $this->_targetNode($node); | 114 $this->_targetNode($node); |
| 115 | 115 |
| 116 my $query = $this->_pathTranslated() || $this->_pathTranslated($this->TranslatePath($this->nodePath)); | 116 my $query = $this->_pathTranslated() || $this->_pathTranslated($this->TranslatePath($this->nodePath)); |
| 117 | 117 |
| 118 my ($foreignNode) = $node->selectNodes(@$query); | 118 my ($foreignNode) = $node->selectNodes(@$query); |
| 119 | 119 |
| 120 my $Source = $ctx && $ctx->{Source} || $this->parentNode; | 120 my $Source = $ctx && $ctx->{Source} || $this->parentNode; |
| 121 | 121 |
| 122 if ($foreignNode) { | 122 if ($foreignNode) { |
| 123 my $value = $this->nodeValue; | 123 my $value = $this->nodeValue; |
| 124 | 124 |
| 125 if ($value) { | 125 if ($value) { |
| 126 $value = FormatMessage($value, { Schema => $this->parentNode, Node => $this->_targetNode, ForeignNode => $foreignNode },\&_resovleProperty); | 126 $value = FormatMessage($value, { Schema => $this->parentNode, Node => $this->_targetNode, ForeignNode => $foreignNode },\&_resovleProperty); |
| 127 } else { | 127 } else { |
| 128 $value = $foreignNode->nodeValue; | 128 $value = $foreignNode->nodeValue; |
| 129 } | 129 } |
| 130 | 130 |
| 131 push @result, new IMPL::DOM::Schema::ValidationError( | 131 push @result, new IMPL::DOM::Schema::ValidationError( |
| 132 Node => $node, | 132 Node => $node, |
| 133 ForeignNode => $foreignNode, | 133 ForeignNode => $foreignNode, |
| 134 Value => $value, | 134 Value => $value, |
| 135 Source => $Source, | 135 Source => $Source, |
| 136 Schema => $this->parentNode, | 136 Schema => $this->parentNode, |
| 137 Message => $this->message | 137 Message => $this->message |
| 138 ) unless $this->op->(_resovleProperty($node,$this->targetProperty),$value); | 138 ) unless $this->op->(_resovleProperty($node,$this->targetProperty),$value); |
| 139 } elsif (not $this->optional) { | 139 } elsif (not $this->optional) { |
| 140 push @result, new IMPL::DOM::Schema::ValidationError( | 140 push @result, new IMPL::DOM::Schema::ValidationError( |
| 141 Node => $node, | 141 Node => $node, |
| 142 Value => '', | 142 Value => '', |
| 143 Source => $Source, | 143 Source => $Source, |
| 144 Schema => $this->parentNode, | 144 Schema => $this->parentNode, |
| 145 Message => $this->message | 145 Message => $this->message |
| 146 ); | 146 ); |
| 147 } | 147 } |
| 148 | 148 |
| 149 $this->_targetNode(undef); | 149 $this->_targetNode(undef); |
| 150 | 150 |
| 151 return @result; | 151 return @result; |
| 152 } | 152 } |
| 153 | 153 |
| 154 sub _resovleProperty { | 154 sub _resovleProperty { |
| 155 my ($node,$prop) = @_; | 155 my ($node,$prop) = @_; |
| 156 | 156 |
| 157 return $node->can($prop) ? $node->$prop() : $node->nodeProperty($prop); | 157 return $node->can($prop) ? $node->$prop() : $node->nodeProperty($prop); |
| 158 } | 158 } |
| 159 | 159 |
| 160 sub _matchRx { | 160 sub _matchRx { |
| 161 $_[0] =~ $_[1]; | 161 $_[0] =~ $_[1]; |
| 162 } | 162 } |
| 163 | 163 |
| 164 sub _notMatchRx { | 164 sub _notMatchRx { |
| 165 $_[0] !~ $_[1]; | 165 $_[0] !~ $_[1]; |
| 166 } | 166 } |
| 167 | 167 |
| 168 sub _equals { | 168 sub _equals { |
| 169 $_[0] == $_[1]; | 169 $_[0] == $_[1]; |
| 170 } | 170 } |
| 171 | 171 |
| 172 sub _notEquals { | 172 sub _notEquals { |
| 173 $_[0] != $_[0]; | 173 $_[0] != $_[0]; |
| 174 } | 174 } |
| 175 | 175 |
| 176 sub _equalsString { | 176 sub _equalsString { |
| 177 $_[0] eq $_[1]; | 177 $_[0] eq $_[1]; |
| 178 } | 178 } |
| 179 | 179 |
| 180 sub _notEqualsString { | 180 sub _notEqualsString { |
| 181 $_[0] ne $_[1]; | 181 $_[0] ne $_[1]; |
| 182 } | 182 } |
| 183 | 183 |
| 184 sub _less { | 184 sub _less { |
| 185 $_[0] < $_[1]; | 185 $_[0] < $_[1]; |
| 186 } | 186 } |
| 187 | 187 |
| 188 sub _greater { | 188 sub _greater { |
| 189 $_[0] > $_[1]; | 189 $_[0] > $_[1]; |
| 190 } | 190 } |
| 191 | 191 |
| 192 sub _lessString { | 192 sub _lessString { |
| 193 $_[0] lt $_[1]; | 193 $_[0] lt $_[1]; |
| 194 } | 194 } |
| 195 | 195 |
| 196 sub _greaterString { | 196 sub _greaterString { |
| 197 $_[0] gt $_[1]; | 197 $_[0] gt $_[1]; |
| 198 } | 198 } |
| 199 | 199 |
| 200 sub _lessEq { | 200 sub _lessEq { |
| 201 $_[0] <= $_[1]; | 201 $_[0] <= $_[1]; |
| 202 } | 202 } |
| 203 | 203 |
| 204 sub _greaterEq { | 204 sub _greaterEq { |
| 205 $_[0] >= $_[1]; | 205 $_[0] >= $_[1]; |
| 206 } | 206 } |
| 207 | 207 |
| 208 1; | 208 1; |
| 209 | 209 |
| 210 __END__ | 210 __END__ |
| 221 Пример типа описания поля с проверочным полем | 221 Пример типа описания поля с проверочным полем |
| 222 | 222 |
| 223 =begin code xml | 223 =begin code xml |
| 224 | 224 |
| 225 <schema> | 225 <schema> |
| 226 <SimpleType type="retype_field"> | 226 <SimpleType type="retype_field"> |
| 227 <Property name="linkedNode" message="Для узла %Node.nodeName% необходимо задать свойство %Source.name%"/> | 227 <Property name="linkedNode" message="Для узла %Node.nodeName% необходимо задать свойство %Source.name%"/> |
| 228 <Compare op="eq" nodePath="sibling:*[nodeName eq '%Node.linkedNode%']"/> | 228 <Compare op="eq" nodePath="sibling:*[nodeName eq '%Node.linkedNode%']"/> |
| 229 </SimpleType> | 229 </SimpleType> |
| 230 </schema> | 230 </schema> |
| 231 | 231 |
| 232 =begin code xml | 232 =begin code xml |
| 233 | 233 |
| 234 =head1 DESCRIPTION | 234 =head1 DESCRIPTION |
