#!/usr/bin/perl

# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Igor Vlasenko <viy@altlinux.org>.
# perl port of maven_depmap.py script by
# Stanislav Ochotnicky <sochotnicky@redhat.com>
#
# this script is used by add_maven_depmap rpm macro to generate
# mapping between maven groupId:artifactId and jar file in our local
# filesystem (i.e. %{_javadir})
# rpm macro expects to find this file as %{_datadir}/java-utils/maven_depmap.py

use strict;
use warnings;
use Getopt::Long;
use File::Basename;

# if slow ...
#use XML::LibXML;
use XML::Simple;

my $append_deps;
my $verbose=0;
my $result = GetOptions (
    "a|append=s"   => \$append_deps,
    "verbose"  => \$verbose
);

sub usage() {
    print "usage: $0 [options] fragment_path pom_path [jar_path]
    Options: -a,--append gid:aid  Additional depmaps to add (gid:aid)
"
}

#### BEGIN DEBUG ####
#use Data::Dumper;
#my $tfragment=&parse_pom($ARGV[0]);
#print Dumper($tfragment);
#exit;
##### END DEBUG #####
#####################

if (@ARGV < 2) {
    die("Incorrect number of arguments");
}

my ($jar_path, $fragment);
my $fragment_path = $ARGV[0];
my $pom_path = $ARGV[1];
if (@ARGV == 3) {
    $jar_path = $ARGV[2];
    $fragment = &parse_pom($pom_path, $jar_path);
} else {
    $fragment = &parse_pom($pom_path);
}
if ($fragment) {
    &output_fragment($fragment_path, $fragment, $append_deps);
} else {
    die "Problem parsing pom file $pom_path. Is it valid maven pom?";
}

# Writes fragment into fragment_path in specialised format compatible with jpp
sub output_fragment {
    my ($fragment_path, $fragment, $additions) = @_;
    my @additions=([$fragment->{-gid}, $fragment->{-aid}]);
    if ($additions) {
        foreach my $add (split (',',$additions)) {
            my ($g, $a) = split(':', $add);
            push @additions, [$g, $a];
	}
    }

    open(my $ffile, '>>',  $fragment_path) || die "$!: can't open $fragment_path";
    foreach my $m (@additions) {
	print $ffile "
<dependency>
    <maven>
        <groupId>$m->[0]</groupId>
        <artifactId>$m->[1]</artifactId>
        <version>$fragment->{-version}</version>
    </maven>
    <jpp>
        <groupId>$fragment->{-local_gid}</groupId>
        <artifactId>$fragment->{-local_aid}</artifactId>
        <version>$fragment->{-version}</version>
    </jpp>
</dependency>
";
    }
    close ($ffile);
}

sub raise_IncompatibleFilenames {
    my ($pom_path, $jar_path) = @_;
    die "Filenames of pom $pom_path and jar $jar_path does not match properly. Check that jar subdirectories match '.' in pom name.";
}

#    """Get resolved (groupId,artifactId) tuple from pom and jar path
#
#    pom name and jar name have to be compatible.
#    JPP.xbean-xbean-main.pom means groupId is "JPP/xbean" and artifactid
#    is "xbean-main". Therefore for jar name to be compatible it has be
#    in %{_javadir}/xbean/xbean-main.jar
#    """
sub _get_jpp_from_filename {
    my ($pom_path, $jar_path) = @_;
    # this is not nice, because macros can change but handling these
    # in rpm macros is ugly as hell
    my @javadirs=("/usr/share/java", "/usr/share/java-jni", "/usr/lib/java",
		  "/usr/lib64/java");
    my $pomname = basename($pom_path);
    my ($jpp_gid,$jpp_aid);
    if ($jar_path) {
        if (not -f $jar_path) {
            die("Jar path $jar_path doesn't exist");
	}	
        my $jarpart; # compound part like ant/ant-oro.jar
        for my $jdir (@javadirs) {
            if ($jar_path =~m!^.*$jdir/(.*)$!) {
                $jarpart = $1;
		last;
	    }
	}
	raise_MissingJarFile($jar_path) if not $jarpart;

        if ($pomname =~ m'^JPP\.') {
	    if ($jarpart !~ m!/!) {
		raise_IncompatibleFilenames($pom_path, $jar_path);
	    }
	    my $dirname = dirname($jarpart);
            $jpp_gid = "JPP/" . $dirname;
	    $jpp_aid = basename($jarpart);
	    $jpp_aid =~s/\.(jar|pom)$//;
            # we assert that jar and pom parts match
            if (not $pomname eq "JPP.$dirname-${jpp_aid}.pom") {
                &raise_IncompatibleFilenames($pom_path, $jar_path);
	    }
	} else {
	    if ($jarpart =~ m!/!) {
		raise_IncompatibleFilenames($pom_path, $jar_path);
	    }
            $jpp_gid = "JPP";
            $jpp_aid = basename($jarpart);
	    $jpp_aid =~s/\.(jar|pom)$//;
            # we assert that jar and pom parts match
            if (not $pomname eq "JPP-${jpp_aid}.pom") {
                &raise_IncompatibleFilenames($pom_path, $jar_path);
	    }
	}
    } else {
        if ($pomname =~ m'^JPP\.') {
            $pomname =~ m'^JPP\.([^-]*?)-.*$';
            $jpp_gid="JPP/$1";
            $pomname =~ m'^JPP\.[^-]*?-(.*)\.pom$';
            $jpp_aid= $1;
	} else {
            $jpp_gid = "JPP";
            $jpp_aid = $pomname;
	    $jpp_aid =~ s/^JPP.//;
	}
    }
    $jpp_aid =~s/\.(jar|pom)$//;
    return $jpp_gid, $jpp_aid;
}

sub raise_PackagingTypeMissingFile {
    my ($pom_path)=@_;
    die "Packaging type is not 'pom' and no artifact path has been provided for pom $pom_path";
}

sub raise_MissingJarFile {
    my ($path)=@_;
    die "Jar seems to be missing in standard directories. Make sure you have installed it ($path)";
}

# """get first xml tag under parent tag within dom"""
sub _get_tag_under_parent {
    my ($dom, $parent, $tag) = @_;
    my $val=$parent->{$tag};
    if (ref $val eq 'ARRAY') {
	$val=$val->[0];
    } elsif (not ref $val) {
	($val)=&Fragment::__strip($val) if $val;
    }
    return $val;
}

# """Returns Fragment class or None if pom file is invalid"""
sub parse_pom {
    my ($pom_file, $jar_file)=@_;
    my $dom = XMLin($pom_file);
    my $project = $dom;
    #print Dumper($project);
    my $proj_packaging = &_get_tag_under_parent($dom, $project, 'packaging');
    # if project packaging is undefined => jar
    # only "pom" packaging type can be without jar_file path otherwise
    # we bail
    if (not $jar_file) {
        if (not $proj_packaging or $proj_packaging ne "pom") {
            &raise_PackagingTypeMissingFile($pom_file);
	}
    }
    my $proj_version = &_get_tag_under_parent($dom, $project, 'version');
    my $proj_gid = &_get_tag_under_parent($dom, $project, 'groupId');
    my $proj_aid = &_get_tag_under_parent($dom, $project, 'artifactId');

    return if not $proj_aid;

    my ($jpp_gid, $jpp_aid) = &_get_jpp_from_filename($pom_file, $jar_file);

    if ($proj_version and $proj_gid) {
	return Fragment->new($proj_gid,
         $proj_aid,
         $proj_version,
         $jpp_gid,
         $jpp_aid
	    );
    }

    my $parent = &_get_tag_under_parent($dom, $project, 'parent');
    return if not $parent;

    my $pgid = &_get_tag_under_parent($dom, $parent, 'groupId');
    if (not $proj_gid) {
        $proj_gid = $pgid;
    }

    my $pversion = &_get_tag_under_parent($dom, $parent, 'version');
    if (not $proj_version) {
        $proj_version = $pversion
    }

    return Fragment->new($proj_gid,
         $proj_aid,
         $proj_version,
         $jpp_gid,
         $jpp_aid
    );
}

package Fragment;
#"""simple structure to hold fragment information"""
sub new {
    my $class=shift;
    my ($gid, $aid, $version, $local_gid,$local_aid)=&__strip(@_);
    my $self={};
    $self->{-gid} = $gid;
    $self->{-aid} = $aid;
    $self->{-version} = $version;
    $self->{-local_gid} = $local_gid;
    $self->{-local_aid} = $local_aid;
    bless $self, $class;
}

sub __strip {
    my @ret=@_;
    return map {s/^\s*//;s/\s*$//;$_} @ret;
}

__END__

