view lib/IMPL/DOM/Schema/NodeSet.pm @ 416:cc2cf8c0edc2 ref20150831

sync
author cin
date Thu, 29 Oct 2015 03:50:25 +0300
parents c6e90e02dd17
children
line wrap: on
line source

package IMPL::DOM::Schema::NodeSet;
use strict;
use warnings;

use IMPL::Const qw(:prop);
use IMPL::declare {
	require => {
		Label => 'IMPL::DOM::Schema::Label',
		ValidationError => 'IMPL::DOM::Schema::ValidationError',
		AnyNode => '-IMPL::DOM::Schema::AnyNode'
	},
	base => [
		'IMPL::DOM::Node' => sub { nodeName => 'NodeSet' }
	],
	props => [
		messageUnexpected => { get => 1, set => 1, dom => 1},
		messageMax => { get => 1, set => 1, dom => 1},
		messageMin => { get => 1, set => 1, dom => 1}
	]
};

sub CTOR {
    my ($this,%args) = @_;
    
    $this->messageMax( $args{messageMax} || 'Too many %node.nodeName% nodes');
    $this->messageMin( $args{messageMin} || '%schemaNode.name% nodes expected');
    $this->messageUnexpected( $args{messageUnexpected} || 'A %node.nodeName% isn\'t allowed in %node.parentNode.path%');
}

sub Validate {
    my ($this,$node,$ctx) = @_;
    
    my @errors;
    
    my %nodes;
    my $anyNode;
    
    foreach (@{$this->childNodes}) {
        if ($_->isa(AnyNode)) {
            $anyNode = {schemaNode => $_, min => $_->minOccur, max => $_->maxOccur eq 'unbounded' ? undef : $_->maxOccur , seen => 0 };
        } else {
            $nodes{$_->name} = {schemaNode => $_, min => $_->minOccur, max => $_->maxOccur eq 'unbounded' ? undef : $_->maxOccur , seen => 0 };
        }
    }
    
    foreach my $child ( @{$node->childNodes} ) {
        if (my $info = $nodes{$child->nodeName} || $anyNode) {
            $info->{seen}++;
            push @errors,ValidationError->new(
                schemaNode => $info->{schemaNode},
                node => $child,
                parent => $node,
                message =>  $this->_MakeLabel($this->messageMax)
            ) if ($info->{max} and $info->{seen} > $info->{max});
            
            if (my @localErrors = $info->{schemaNode}->Validate($child)) {
                push @errors,@localErrors;
            }
        } else {
            push @errors, ValidationError->new(
                node => $child,
                parent => $node,
                message => $this->_MakeLabel($this->messageUnexpected)
            )
        }
    }
    
    foreach my $info (values %nodes) {
        push @errors, ValidationError->new(
            schemaNode => $info->{schemaNode},
            parent => $node,
            message => $this->_MakeLabel($this->messageMin)
        ) if $info->{min} > $info->{seen};
    }
    
    return @errors;
}

sub _MakeLabel {
	my ($this,$label) = @_;
	
	if ($label =~ /^ID:(\w+)$/) {
		return Label->new($this->document->stringMap, $1);
	} else {
		return $label;
	}
}

1;

__END__

=pod

=head1 DESCRIPTION

Содержимое для сложного узла. Порядок не важен. Дочерними элементами могут быть
только C<IMPL::DOM::Schema::ComplexNode> и C<IMPL::DOM::Schema::SimpleNode>.

При проверке данного правила, проверяются имеющиеся элементы на соответсие схемы
и количества встречаемости, после чего проверяются количественные ограничения
для несуществующих элементов.

=cut