#!/usr/bin/perl

# Validates a file or directory against loncapa.xsd with libxml2

use strict;
use utf8;
use warnings;

use File::Basename;
use Try::Tiny;
use XML::LibXML;


binmode(STDOUT, ':encoding(UTF-8)');
binmode(STDERR, ':encoding(UTF-8)');

if (scalar(@ARGV) != 1) {
  print STDERR "Usage: perl validate_xml.pl file|directory\n";
  exit(1);
}

# find the command-line argument encoding
use I18N::Langinfo qw(langinfo CODESET);
my $codeset = langinfo(CODESET);
use Encode qw(decode);
@ARGV = map { decode $codeset, $_ } @ARGV;

my $pathname = "$ARGV[0]";

my $script_dir = dirname(__FILE__);
my $xmlschema = XML::LibXML::Schema->new(location => $script_dir.'/loncapa.xsd');

if (-d "$pathname") {
  validate_dir($pathname);
} elsif (-f $pathname) {
  validate_file($pathname);
}


# Validates a directory recursively, selecting only .(problem|exam|survey|html|library).xml files.
sub validate_dir {
  my ($dirpath) = @_;
  
  opendir (my $dh, $dirpath) or die $!;
  while (my $entry = readdir($dh)) {
    next if ($entry =~ m/^\./); # ignore entries starting with a period
    my $pathname = $dirpath.'/'.$entry;
    if (-d $pathname) {
      validate_dir($pathname);
    } elsif (-f $pathname) {
      if ($pathname =~ /\.(problem|exam|survey|html?|library)\.xml$/) {
        validate_file($pathname);
      }
    }
  }
  closedir($dh);
}

# Validates a file against loncapa.xsd with libxml2
sub validate_file {
  my ($pathname) = @_;
  
  my $doc = XML::LibXML->load_xml(location => $pathname);
  try {
    $xmlschema->validate($doc);
    print "$pathname is valid\n";
  } catch {
    $_ =~ s/%20/ /g;
    print "$_\n";
  }
}
