#!/usr/bin/perl
#
# Convert a JBuilder JPX file to an Eclipse .project

use warnings;
use strict;

use XML::Simple;
use Getopt::Long;
use Data::Dumper;
use File::Basename;
use File::Spec;
use Cwd 'abs_path';


our %OPTIONS = ();
GetOptions(\%OPTIONS, "help|?",
                      "dryrun|n");
die <<EOM if $OPTIONS{"help"} or not @ARGV;
jpx2eclipse [OPTIONS] <jpx>
~~~~~~~~~~~
Options:
    --help, -h           This message
    --dryrun, -n         Don\'t write modifications to disk

Please report bugs to <andrew\@bleb.org>. Thanks.

EOM

our $JPX    = $ARGV[0] or die "no JPX specified.\n";
my $xml     = XMLin($JPX) or die "unable to parse XML.\n";
chdir(dirname($JPX));

our $DEST   = $xml->{property}->{WorkingDirectory}->{value} or die "unable to find destination dir.\n";
our $NAME   = (fileparse($JPX,qr{\.jpx}))[0];
our $TOMCAT = ($xml->{property}->{'server.name'}->{value} || '') =~ /Tomcat/i;

#print Dumper($xml);

&writeProject($NAME, $TOMCAT);
&writeClasspath($xml);
&writeTomcat($xml) if $TOMCAT;
&writeAnt($xml);

exit;


# -----------------------------------------------------------------------
# Write the .project file
# -----------------------------------------------------------------------
sub writeProject {
	my ($name, $tomcat) = @_;
	
	if ($OPTIONS{dryrun}) {
	  *OUT = \*STDOUT;
	  warn "not writing $DEST/.project\n";
	} else {
		open(OUT, ">$DEST/.project") or die "Unable to open .project: $!\n";
	}
	
	print OUT <<EOM;
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
        <name>$NAME</name>
        <comment></comment>
        <projects>
        </projects>
        <buildSpec>
                <buildCommand>
                        <name>org.eclipse.jdt.core.javabuilder</name>
                        <arguments>
                        </arguments>
                </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.jdt.core.javanature</nature>
EOM
  print OUT "                <nature>com.sysdeo.eclipse.tomcat.tomcatnature</nature>\n" if $tomcat;
  print OUT "        </natures>\n</projectDescription>\n";
  
  if (!$OPTIONS{dryrun}) {
  	close(OUT) or die "Unable to close .project: $!\n";
  }
}


# -----------------------------------------------------------------------
# Write the .tomcatPlugin file, if this is a J2EE app
# -----------------------------------------------------------------------
sub writeTomcat {
	my ($data) = @_;
	  
	if ($OPTIONS{dryrun}) {
	  *OUT = \*STDOUT;
	  warn "not writing $DEST/.tomcatplugin\n";
	} else {
		open(OUT, ">$DEST/.tomcatplugin") or die "Unable to open .tomcatplugin: $!\n";
	}
	
	my $context;
  foreach my $n (keys(%{ $xml->{node} })) {
  	if ($xml->{node}->{$n}->{type} eq 'WebModuleNode') {
  		$context = $n;
  		last;
  	}
  }
  
  $context ||= $NAME;
  my $warloc = abs_path(dirname(File::Spec->canonpath( $JPX )));
	$warloc =~ s!/cygdrive/(\w)/!$1:/!;

	print OUT <<EOM;
<?xml version="1.0" encoding="UTF-8"?>
<tomcatProjectProperties>
    <rootDir>/</rootDir>
    <exportSource>false</exportSource>
    <reloadable>true</reloadable>
    <redirectLogger>false</redirectLogger>
    <updateXml>true</updateXml>
    <warLocation>$warloc/${context}.war</warLocation>
    <extraInfo></extraInfo>
    <webPath>/$context</webPath>
EOM

   if (defined(@{ $data->{_libs} })) {
    print OUT "    <webClassPathEntries>\n";
    foreach my $path (@{ $data->{_libs} }) {
      print OUT "        <webClassPathEntry>$path</webClassPathEntry>\n";
    }
    print OUT "    </webClassPathEntries>\n";
  }
  
  print "</tomcatProjectProperties>\n";
  $data->{_warloc} = "$warloc/${context}.war";
 
  if (!$OPTIONS{dryrun}) {
  	close(OUT) or die "Unable to close .tomcatplugin: $!\n";
  }
}


# -----------------------------------------------------------------------
# Write the .classpath file
# -----------------------------------------------------------------------
sub writeClasspath {
  my ($data) = @_;
  
  my $srcdir = $data->{property}->{SourcePath}->{value};
  my $outdir = $data->{property}->{OutPath}->{value};
  
  $srcdir =~ s/^$DEST//;
  $outdir = "$srcdir/../$outdir" unless $outdir =~ s/^$DEST//;
  
  $srcdir =~ s!^/!!;
  $outdir =~ s!^/!!;
	  
	if ($OPTIONS{dryrun}) {
	  *OUT = \*STDOUT;
	  warn "not writing $DEST/.classpath\n";
	} else {
		open(OUT, ">$DEST/.classpath") or die "Unable to open .classpath: $!\n";
	}
	
  print OUT <<EOM;
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
        <classpathentry kind="src" path="$srcdir"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="output" path="$outdir"/>
EOM

  print OUT <<EOT if $TOMCAT;
        <classpathentry kind="var" path="TOMCAT_HOME/common/lib/servlet-api.jar"/>
        <classpathentry kind="var" path="TOMCAT_HOME/common/lib/jasper-runtime.jar"/>
        <classpathentry kind="var" path="TOMCAT_HOME/common/lib/jsp-api.jar"/>
EOT

  my @libs = split(/;/, $data->{property}->{Libraries}->{value});
  my %available = ( JUnit => ["var#JUNIT_HOME/junit.jar"] );
  while (<*.library>) {
  	my $xml  = XMLin($_, ForceArray => [ 'path' ]);
  	my $name = $xml->{fullname} || $_;
  	$name =~ s/[^\w\. ]+//;
  	next if $name =~ /Servlet/i or !defined($xml->{class}->{path});
  	
  	my $required = $xml->{required} || '';
  	$required =~ s/[^\w ]+//;
  	push @libs, $required if $required;
  	
  	my @e = ();
    foreach my $p (@{ $xml->{class}->{path} }) {
    	push @e, "lib#$1" if $p =~ /^\[(.*)\]$/;
    }
    
    $available{$name} = \@e;
  }

  foreach my $l (@libs) {
  	next if $l =~ /Servlet/i;
  	unless ($available{$l}) {
  		warn "Unable to find library $l...\n";
  		next;
  	}
  	
    foreach my $e (@{ $available{$l} }) {
    eval {
    	  $e =~ s/^(.*?)#//;
    	  my ($type, $path) = ($1, $e);
    	  $path = abs_path($e) if $type eq 'lib';
    	  $path =~ s!/cygdrive/(\w)/!$1:/!;
        print OUT '        <classpathentry kind="'.$type.'" path="'.$path."\"/>\n";
        push @{ $data->{_libs} }, $path if $type eq 'lib';
      }; if ($@) {
        warn $@;
      }
  	}
  }
  
  print OUT "</classpath>\n";
  if (!$OPTIONS{dryrun}) {
  	close(OUT) or die "Unable to close .classpath: $!\n";
  }
}


# -----------------------------------------------------------------------
# Write the build.xml file if required
# -----------------------------------------------------------------------
sub writeAnt {
	my ($data) = @_;
	
	return unless $TOMCAT and $data->{_libs};
	
	# -- Don't clobber the file if modified...
	#
	if (open(IN, "<$DEST/build.xml")) {
		my $read = '';
		while(<IN>) { $read .= $_; }
		close(IN);
		
		return unless $read =~ /<!-- Remove this line to prevent auto-clobbering -->/i;
	}
	
	# -- Create the output stream...
	#
	if ($OPTIONS{dryrun}) {
	  *OUT = \*STDOUT;
	  warn "not writing $DEST/build.xml\n";
	} else {
		open(OUT, ">$DEST/build.xml") or die "Unable to open build.xml: $!\n";
	}
	
	# -- Write the file...
	#
	my $date   = localtime();
	my $warloc = $data->{_warloc};
	print OUT <<EOM;
<project name="$NAME">
  <description>Ant build file for $NAME WAR file.
  Auto-generated by jpx2eclipse on $date</description>

  <!-- REMOVE THIS LINE TO PREVENT AUTO-CLOBBERING -->

  <property name="pkg" location="WEB-INF"/>
  <property name="dist" location="$warloc"/> 

  <defaultexcludes add="**/*.bak" echo="true"/>
  <defaultexcludes add="**/*~"/>
  <defaultexcludes add="**/.*"/>
  <defaultexcludes add="**/.svn"/>
  <defaultexcludes add="**/src/**"/>
  <defaultexcludes add="**/work/**"/>

  <!-- Build WAR file -->
  <target name="package" description="Produce a WAR file suitable for deployment">
    <war destfile="\${dist}" webxml="\${pkg}/web.xml">
    <webinf dir="\${pkg}" excludes="**/web.xml"/>
    <fileset dir="\${basedir}" excludes="**/WEB-INF/**"/>
EOM

  foreach my $e (@{ $data->{_libs} }) {
  	my ($name, $dir, undef) = fileparse($e);
  	
  	print OUT "    <lib file=\"$e\"/>\n";#dir=\"$dir\" files=\"$name\"/>\n";
  }
  
  print OUT <<EOM;
    </war>
  </target>
</project>
EOM
  
  if (!$OPTIONS{dryrun}) {
  	close(OUT) or die "Unable to close build.xml: $!\n";
  }
}