#! /usr/bin/env perl use v5.10.1; use strict; use warnings; use Getopt::Long; use Pod::Usage; use XML::LibXML; # Extract IDs and remove duplicates sub extract_ids (\@@) { my $ids = shift @_; foreach my $node (@_) { my $att = $node->getAttribute("id"); if (defined $att) { if (grep {$_ eq $att} @$ids) { $node->removeAttribute("id"); } else { push @$ids, $att; } } } } # Parse arguments my $input; my $output; my $help; my $man; GetOptions ("output|O=s" => \$output, "help|h|?" => \$help, "man" => \$man, "" => \$input) or pod2usage(2); pod2usage(1) if $help; pod2usage(-exitval => 0, -verbose => 2) if $man; # Open defense XML file my $xmlin; if (defined $input || $#ARGV == -1) { $xmlin = *STDIN; } else { open $xmlin, "<", $ARGV[0] or die $!; } binmode $xmlin; my $dom = XML::LibXML->load_xml(IO => $xmlin); close $xmlin unless $xmlin eq *STDIN; # First, get all existing ID and remove duplicates my @ids; extract_ids @ids, $dom->getElementsByTagName("group"); extract_ids @ids, $dom->getElementsByTagName("question"); extract_ids @ids, $dom->getElementsByTagName("answer"); # Then, attribute an ID to node that hasn't my $grp_i = 0; foreach my $group ($dom->getElementsByTagName("group")) { my $cur_gid = $group->getAttribute("id"); if (!defined $cur_gid) { do { $cur_gid = "defg".$grp_i; $grp_i += 1; } while (grep {$_ eq $cur_gid} @ids); $group->setAttribute("id", $cur_gid); } else { $grp_i += 1; } my $qst_i = 0; foreach my $question ($group->getElementsByTagName("question")) { my $cur_qid = $question->getAttribute("id"); if (!defined $cur_qid) { do { $cur_qid = $cur_gid."q".$qst_i; $qst_i += 1; } while (grep {$_ eq $cur_qid} @ids); $question->setAttribute("id", $cur_qid); } else { $qst_i += 1; } my $ans_i = 0; foreach my $answer ($question->getElementsByTagName("answer")) { my $cur_aid = $answer->getAttribute("id"); if (!defined $cur_aid) { do { $cur_aid = $cur_qid."a".$ans_i; $ans_i += 1; } while (grep {$_ eq $cur_aid} @ids); $answer->setAttribute("id", $cur_aid); } else { $ans_i += 1; } } } } # Save defense XML file my $xmlout; if (defined $output) { open $xmlout, '>', $output; binmode $xmlout; } else { $xmlout = *STDOUT; } print {$xmlout} $dom->toString(); close $xmlout unless $xmlout eq *STDOUT; __END__ =head1 NAME prepare_xml.pl - Prepare defense XML by adding id to groups, questions and answers =head1 SYNOPSIS prepare_xml.pl [options] [file] =head1 DESCRIPTION Parse the XML file given (or stdin if no file is given) and add id to groups, questions and answers that have any or duplicate id. Options: -output=file.xml save prepared XML to this location -help brief help message -man full documentation =head1 OPTIONS =over 8 =item B<-output=file.xml> Save the prepared XML to a file instead of printing it on standard output. =item B<-help> Print a brief help message and exits. =item B<-man> Prints the manual page and exits. =back =cut