XML-Compare

 view release on metacpan or  search on metacpan

lib/XML/Compare.pm  view on Meta::CPAN

                _outit($l, 'namespaceURIs are different', $node1->namespaceURI(), $node2->namespaceURI());
                _die $l, 'namespaceURIs are different: (%s, %s)', $ns1, $ns2;
            }
        }
        elsif ( (!defined $ns1) and (!defined $ns2) ) {
            _same($l, 'namespaceURI (not defined for either node)');
        }
        else {
	    if ( $self->namespace_strict or defined $ns1 ) {
		_outit($l, 'namespaceURIs are defined/not defined', $ns1, $ns2);
		_die $l, 'namespaceURIs are defined/not defined: (%s, %s)', ($ns1 || '[undef]'), ($ns2 || '[undef]');
	    }
        }
    }

    # check the attribute list is the same length
    if ( $has->{attributes}{ref $node1} ) {

	my $in = $self->_ignore_nodes;
        # get just the Attrs and sort them by namespaceURI:localname
        my @attr1 = sort { _fullname($a) cmp _fullname($b) }
	    grep { (!$in) or (!exists $in->{$_->nodePath}) }
		grep { defined and $_->isa('XML::LibXML::Attr') }
		    $node1->attributes();

        my @attr2 = sort { _fullname($a) cmp _fullname($b) }
	    grep { (!$in) or (!exists $in->{$_->nodePath}) }
		grep { defined and $_->isa('XML::LibXML::Attr') }
		    $node2->attributes();

        if ( scalar @attr1 == scalar @attr2 ) {
            _same($l, 'attribute length (' . (scalar @attr1) . ')');
        }
        else {
            _die $l, 'attribute list lengths differ: (%d, %d)', scalar @attr1, scalar @attr2;
        }

        # for each attribute, check they are all the same
        my $total_attrs = scalar @attr1;
        for (my $i = 0; $i < scalar @attr1; $i++ ) {
            # recurse down (either an exception will be thrown, or all are correct
            $self->_are_nodes_same( [@$l,'@'.$attr1[$i]->name], $attr1[$i], $attr2[$i] );
        }
    }

    my $in = $self->_ignore_nodes;

    # don't need to compare or care about Comments
    my @nodes1 = grep { (!$in) or (!exists $in->{$_->nodePath}) }
	grep { (not $_->isa('XML::LibXML::Comment')) and
		   not ( $_->isa("XML::LibXML::Text") && ($_->data =~ /\A\s*\Z/) )
	       }
	    $node1->childNodes();

    my @nodes2 = grep { (!$in) or (!exists $in->{$_->nodePath}) }
	grep { (not $_->isa('XML::LibXML::Comment')) and
		   not ( $_->isa("XML::LibXML::Text") && ($_->data =~ /\A\s*\Z/) )
	       } $node2->childNodes();

    # firstly, convert all CData nodes to Text Nodes
    @nodes1 = _convert_cdata_to_text( @nodes1 );
    @nodes2 = _convert_cdata_to_text( @nodes2 );

    # append all the consecutive Text nodes
    @nodes1 = _squash_text_nodes( @nodes1 );
    @nodes2 = _squash_text_nodes( @nodes2 );

    # check that the nodes contain the same number of children
    if ( @nodes1 != @nodes2 ) {
        _die $l, 'different number of child nodes: (%d, %d)', scalar @nodes1, scalar @nodes2;
    }

    # foreach of it's children, compare them
    my $total_nodes = scalar @nodes1;
    for (my $i = 0; $i < $total_nodes; $i++ ) {
        # recurse down (either an exception will be thrown, or all are correct
	my $nn = $nodes1[$i]->nodeName;
	if ( grep { $_->nodeName eq $nn }
		 @nodes1[0..$i-1, $i+1..$#nodes1] ) {
	    $nn .= "[position()=".($i+1)."]";
	}
	$nn =~ s{#text}{text()};
        $self->_are_nodes_same( [@$l,$nn], $nodes1[$i], $nodes2[$i] );
    }

    _msg($l, '/');
    return 1;
}

# takes an array of nodes and converts all the CDATASection nodes into Text nodes
sub _convert_cdata_to_text {
    my @nodes = @_;
    my @new;
    foreach my $n ( @nodes ) {
	if ( ref $n eq 'XML::LibXML::CDATASection' ) {
	    $n = XML::LibXML::Text->new( $n->data() );
	}
	push @new, $n;
    }
    return @new;
}

# takes an array of nodes and concatenates all the Text nodes together
sub _squash_text_nodes {
    my @nodes = @_;
    my @new;
    my $last_type = '';
    foreach my $n ( @nodes ) {
	if ( $last_type eq 'XML::LibXML::Text' and ref $n eq 'XML::LibXML::Text' ) {
	    $n = XML::LibXML::Text->new( $new[-1]->data() . $n->data() );
	    $new[-1] = $n;
	}
	else {
	    push @new, $n;
	}
	$last_type = ref $n;
    }
    return @new;
}

sub _fullname {
    my ($node) = @_;
    my $name = '';
    $name .= $node->namespaceURI() . ':' if $node->namespaceURI();
    $name .= $node->localname();
    # print "name=$name\n";
    return $name;
}

sub _same {
    my ($l, $msg) = @_;
    return unless $VERBOSE;
    print '' . ('  ' x (@$l+1)) . "= $msg\n";
}

sub _msg {
    my ($l, $msg) = @_;
    return unless $VERBOSE;
    print ' ' . ('  ' x (@$l)) ._xpath($l). " $msg\n";
}

sub _outit {
    my ($l, $msg, $v1, $v2) = @_;
    return unless $VERBOSE;
    print '' . ('  ' x @$l) . "! " ._xpath($l)." $msg:\n";
    print '' . ('  ' x @$l) . '. ' . ($v1 || '[undef]') . "\n";
    print '' . ('  ' x @$l) . '. ' . ($v2 || '[undef]') . "\n";
}

1;
__END__



( run in 0.615 second using v1.01-cache-2.11-cpan-39bf76dae61 )