Factored out the code that will be shared between GenServiceTask and
GenReceiverTask (not yet implemented). Modified the task so that it can load the service classes via a classpath declared inside ant, avoiding the need to put project classes in Ant's classpath. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3240 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
-467
@@ -1,467 +0,0 @@
|
|||||||
#!/usr/bin/perl -w
|
|
||||||
#
|
|
||||||
# $Id: genservice,v 1.6 2004/10/13 19:29:12 andrzej Exp $
|
|
||||||
#
|
|
||||||
# This script is used to generate invocation service marshaller and
|
|
||||||
# unmarshaller classes based on invocation service interface definitions.
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use Getopt::Long;
|
|
||||||
use Template;
|
|
||||||
use File::Basename;
|
|
||||||
use POSIX qw(strftime);
|
|
||||||
|
|
||||||
# use Dumpvalue;
|
|
||||||
|
|
||||||
my $usage = "Usage: $0 [--verbose] [--force] [--classpath classpath] " .
|
|
||||||
"[--sourcedir sourcedir] <sourcedir/foo/bar/FooService.java> [...]\n";
|
|
||||||
|
|
||||||
# get our options
|
|
||||||
my $verbose;
|
|
||||||
my $force;
|
|
||||||
my $classpath;
|
|
||||||
my $sourcedir = ".";
|
|
||||||
GetOptions("verbose" => \$verbose,
|
|
||||||
"force" => \$force,
|
|
||||||
"classpath=s" => \$classpath,
|
|
||||||
"sourcedir=s" => \$sourcedir,
|
|
||||||
);
|
|
||||||
|
|
||||||
# some standard business
|
|
||||||
my $ISVC_CNAME = "com.threerings.presents.client.InvocationService";
|
|
||||||
|
|
||||||
# make sure we have at least one source file argument
|
|
||||||
die $usage unless (@ARGV);
|
|
||||||
|
|
||||||
# locate a few necessary other files
|
|
||||||
my $script = basename($0);
|
|
||||||
my $rootdir = dirname($0); $rootdir =~ s:bin$:.:g;
|
|
||||||
my $marshtmpl = "$rootdir/lib/marshaller.tmpl";
|
|
||||||
my $disptmpl = "$rootdir/lib/dispatcher.tmpl";
|
|
||||||
|
|
||||||
# now process our source files
|
|
||||||
my %imports;
|
|
||||||
my $srcfile;
|
|
||||||
my $spackage;
|
|
||||||
|
|
||||||
while ($srcfile = shift) {
|
|
||||||
# do a quick check to see if the marshaller file is newer than the
|
|
||||||
# service file in which case we can stop now
|
|
||||||
my $tpath = $srcfile;
|
|
||||||
$tpath =~ s/Service/Marshaller/;
|
|
||||||
$tpath =~ s:/client/:/data/:;
|
|
||||||
my $dmtime = (stat($tpath))[9];
|
|
||||||
my $smtime = (stat($srcfile))[9];
|
|
||||||
if (!$force && (defined $dmtime) && ($dmtime > $smtime)) {
|
|
||||||
my @foo = split(/\//, $srcfile);
|
|
||||||
my $rfile = pop @foo;
|
|
||||||
warn "Skipping ...$rfile as generated files are up to date.\n"
|
|
||||||
if ($verbose);
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
# convert the source file given on the command line to a class name
|
|
||||||
open(SRC, "$srcfile") or die "$script: Unable to read '$srcfile': $!\n";
|
|
||||||
while (<SRC>) {
|
|
||||||
if (/^package (\S+);/) {
|
|
||||||
$spackage = $1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$srcfile =~ m:/([^/]+)\.java$:;
|
|
||||||
my $class = "$spackage.$1";
|
|
||||||
|
|
||||||
# run javap on the interface class
|
|
||||||
my $jpcp = (defined $classpath) ? "-classpath $classpath" : "";
|
|
||||||
my $jpcmd = "/usr/local/j2sdk1.4.1/bin/javap $jpcp $class";
|
|
||||||
|
|
||||||
# clear out the imports table
|
|
||||||
%imports = ();
|
|
||||||
|
|
||||||
my $ifcname;
|
|
||||||
my @methods;
|
|
||||||
my @listeners;
|
|
||||||
my $listener;
|
|
||||||
|
|
||||||
my $methcount = 1;
|
|
||||||
my $lmethcount = 0;
|
|
||||||
|
|
||||||
open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n";
|
|
||||||
while (<JPO>) {
|
|
||||||
if (/^public interface (\S+) extends ([^, ]+)/) {
|
|
||||||
$ifcname = $1;
|
|
||||||
$imports{$ifcname} = 1;
|
|
||||||
my $sname = $2;
|
|
||||||
|
|
||||||
# if this interface does not extend InvocationService, we
|
|
||||||
# don't want to do nothin'
|
|
||||||
die "$script: $ifcname does not extend " .
|
|
||||||
"InvocationService\n (extends $sname).\n"
|
|
||||||
if ($sname ne $ISVC_CNAME);
|
|
||||||
|
|
||||||
} elsif (/^ public abstract void (\S+)\(([^\)]*)\)/) {
|
|
||||||
my $methname = $1;
|
|
||||||
my $rawargs = $2; $rawargs =~ s:\$:.:g;
|
|
||||||
my @methargs = split(/, /, $rawargs);
|
|
||||||
grab_imports(@methargs);
|
|
||||||
my @trimmed_args = trim_args(@methargs);
|
|
||||||
my @unwrap_args = @trimmed_args;
|
|
||||||
shift @unwrap_args;
|
|
||||||
push @methods, {
|
|
||||||
"mname" => $methname,
|
|
||||||
"mcode" => to_caps($methname),
|
|
||||||
"mcodeval" => $methcount++,
|
|
||||||
"arglist" => gen_arglist(@trimmed_args),
|
|
||||||
"wrapped_args" => wrap_arglist(1, @trimmed_args),
|
|
||||||
"unwrapped_args" => unwrap_arglist(@unwrap_args),
|
|
||||||
"listener_args" => gen_listener_args(@trimmed_args),
|
|
||||||
};
|
|
||||||
|
|
||||||
} elsif (/^\s+public static interface \S+\. (\S+) extends (\S+)\. (\S+)/) {
|
|
||||||
my $inner = $1;
|
|
||||||
my $eouter = $2;
|
|
||||||
my $einner = $3;
|
|
||||||
|
|
||||||
if ($eouter ne $ISVC_CNAME) {
|
|
||||||
warn "Unrecognized inner interface $inner (extends $2.$3). " .
|
|
||||||
"Skipping.\n";
|
|
||||||
undef $listener;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
die "$script: Invalid callback interface name $inner.\n" .
|
|
||||||
"Must be of the form <foo>Listener\n"
|
|
||||||
if ($inner !~ m/Listener$/);
|
|
||||||
|
|
||||||
# create a new listener table to hold info on this listener class
|
|
||||||
$inner =~ s:Listener$::;
|
|
||||||
my @lmethods = ();
|
|
||||||
$listener = {
|
|
||||||
"name" => $inner,
|
|
||||||
"methods" => \@lmethods,
|
|
||||||
};
|
|
||||||
push @listeners, $listener;
|
|
||||||
$lmethcount = 0;
|
|
||||||
|
|
||||||
} elsif (/^ public abstract void (\S+)\(([^\)]*)\)/) {
|
|
||||||
my $methname = $1;
|
|
||||||
my $rawargs = $2; $rawargs =~ s:\$:.:g;
|
|
||||||
my @methargs = split(/, /, $rawargs);
|
|
||||||
|
|
||||||
# skip this method declaration if we're not parsing a valid
|
|
||||||
# listener inner interface declaration
|
|
||||||
next unless (defined $listener);
|
|
||||||
|
|
||||||
grab_imports(@methargs);
|
|
||||||
my @trimmed_args = trim_args(@methargs);
|
|
||||||
my $lmethod = {
|
|
||||||
"mname" => $methname,
|
|
||||||
"mcode" => to_caps($methname),
|
|
||||||
"mcodeval" => ++$lmethcount,
|
|
||||||
"arglist" => gen_arglist(@trimmed_args),
|
|
||||||
"wrapped_args" => wrap_arglist(0, @trimmed_args),
|
|
||||||
"unwrapped_args" => unwrap_arglist(@trimmed_args),
|
|
||||||
};
|
|
||||||
push @{$listener->{"methods"}}, $lmethod;
|
|
||||||
|
|
||||||
} elsif (/^Compiled from/ ||
|
|
||||||
/ACC_SUPER/ ||
|
|
||||||
/^\s*[{}]\s*$/) {
|
|
||||||
# skip it
|
|
||||||
|
|
||||||
} else {
|
|
||||||
warn "$script: ?: $_";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
die "$script: Unable to parse primary interface definition.\n"
|
|
||||||
unless (defined $ifcname);
|
|
||||||
|
|
||||||
print "$script: Processing $ifcname...\n";
|
|
||||||
|
|
||||||
# extract the package name
|
|
||||||
my $ifcpkg = $ifcname;
|
|
||||||
$ifcpkg =~ s/\.[^.]*$//g;
|
|
||||||
|
|
||||||
# extract the service name
|
|
||||||
my $svcname = substr($ifcname, length($ifcpkg)+1);
|
|
||||||
$svcname =~ s/Service$//g;
|
|
||||||
die "$script: Unable to discern service name from $ifcname.\n".
|
|
||||||
"Service interface definitions should be named <foo>Service.\n"
|
|
||||||
if ($svcname eq $ifcname);
|
|
||||||
|
|
||||||
# assume everything's in the same package to start
|
|
||||||
my $marshpkg = $ifcpkg;
|
|
||||||
my $disppkg = $ifcpkg;
|
|
||||||
|
|
||||||
# if the interface is in a 'client' package, we'll put the marshaller
|
|
||||||
# in the associated 'data' package and the dispatcher in the 'server'
|
|
||||||
# package
|
|
||||||
if ($ifcpkg =~ m/\.client$/) {
|
|
||||||
$marshpkg =~ s/\.client$/.data/g;
|
|
||||||
$disppkg =~ s/\.client$/.server/g;
|
|
||||||
}
|
|
||||||
|
|
||||||
# create a template processor
|
|
||||||
my $ttconfig = {
|
|
||||||
RELATIVE => 1,
|
|
||||||
ABSOLUTE => 1,
|
|
||||||
};
|
|
||||||
my $tt = Template->new($ttconfig);
|
|
||||||
my $genstamp = strftime("%T %D", localtime);
|
|
||||||
|
|
||||||
# now we generate the <foo>Marshaller class definition
|
|
||||||
my $marshpath = $marshpkg;
|
|
||||||
$marshpath =~ s:\.:/:g;
|
|
||||||
$marshpath = "$sourcedir/$marshpath/$svcname" . "Marshaller.java";
|
|
||||||
|
|
||||||
print "$script: Generating marshaller:\n" .
|
|
||||||
" $marshpkg.${svcname}Marshaller in\n" .
|
|
||||||
" $marshpath\n" if ($verbose);
|
|
||||||
|
|
||||||
# populate a data table with all the data we'll need to fill out our
|
|
||||||
# marshaller template file
|
|
||||||
my %mimports = %imports;
|
|
||||||
$mimports{"com.threerings.presents.client.Client"} = 1;
|
|
||||||
$mimports{"com.threerings.presents.data.InvocationMarshaller"} = 1;
|
|
||||||
$mimports{"com.threerings.presents.dobj.InvocationResponseEvent"} = 1;
|
|
||||||
my @mimportsl = sort keys %mimports;
|
|
||||||
my %mdata = ( "idstr" => slurp_idstr($marshpath),
|
|
||||||
"imports" => \@mimportsl,
|
|
||||||
"name" => $svcname,
|
|
||||||
"package" => $marshpkg,
|
|
||||||
"listeners" => \@listeners,
|
|
||||||
"methods" => \@methods,
|
|
||||||
"genstamp" => $genstamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
# my $dumper = Dumpvalue->new;
|
|
||||||
# $dumper->dumpValue(\%mdata);
|
|
||||||
|
|
||||||
$tt->process($marshtmpl, \%mdata, $marshpath) or
|
|
||||||
die "$script: Unable to process template file '$marshtmpl': " .
|
|
||||||
$tt->error() . "\n";
|
|
||||||
|
|
||||||
# now we generate the <foo>Dispatcher class definition
|
|
||||||
my $disppath = $disppkg;
|
|
||||||
$disppath =~ s:\.:/:g;
|
|
||||||
$disppath = "$sourcedir/$disppath/$svcname" . "Dispatcher.java";
|
|
||||||
|
|
||||||
print "$script: Generating dispatcher:\n" .
|
|
||||||
" $disppkg.${svcname}Dispatcher in\n" .
|
|
||||||
" $disppath\n" if ($verbose);
|
|
||||||
|
|
||||||
# populate a data table with all the data we'll need to fill out our
|
|
||||||
# dispatcher template file
|
|
||||||
my %dimports = %imports;
|
|
||||||
$dimports{"com.threerings.presents.data.ClientObject"} = 1;
|
|
||||||
$dimports{"com.threerings.presents.data.InvocationMarshaller"} = 1;
|
|
||||||
$dimports{"com.threerings.presents.server.InvocationDispatcher"} = 1;
|
|
||||||
$dimports{"com.threerings.presents.server.InvocationException"} = 1;
|
|
||||||
$dimports{"$marshpkg.${svcname}Marshaller"} = 1;
|
|
||||||
my @dimportsl = sort keys %dimports;
|
|
||||||
my %ddata = ( "idstr" => slurp_idstr($disppath),
|
|
||||||
"imports" => \@dimportsl,
|
|
||||||
"name" => $svcname,
|
|
||||||
"package" => $disppkg,
|
|
||||||
"listeners" => \@listeners,
|
|
||||||
"methods" => \@methods,
|
|
||||||
"genstamp" => $genstamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
# my $dumper = Dumpvalue->new;
|
|
||||||
# $dumper->dumpValue(\%ddata);
|
|
||||||
|
|
||||||
$tt->process($disptmpl, \%ddata, $disppath) or
|
|
||||||
die "$script: Unable to process template file '$disptmpl': " .
|
|
||||||
$tt->error() . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Extracts the imports from the supplied list of arguments.
|
|
||||||
#
|
|
||||||
sub grab_imports {
|
|
||||||
my $class;
|
|
||||||
foreach $class (@_) {
|
|
||||||
# skip classes in java.lang
|
|
||||||
next if ($class =~ m/^java\.lang\.[^.]+$/);
|
|
||||||
# skip primitive types
|
|
||||||
next unless ($class =~ m/\./);
|
|
||||||
|
|
||||||
# trim array delimiters
|
|
||||||
my $tclass = $class;
|
|
||||||
$tclass =~ s:[\[\]]::g;
|
|
||||||
|
|
||||||
# stuff the class into our import table
|
|
||||||
$imports{$tclass} = 1;
|
|
||||||
|
|
||||||
# if this class is a listener and not in the same package as the
|
|
||||||
# service class we're currently processing, we need to import its
|
|
||||||
# marshaller as well
|
|
||||||
if ($tclass =~ m/Listener$/ &&
|
|
||||||
$tclass !~ m/^$spackage/ &&
|
|
||||||
$tclass !~ $ISVC_CNAME) {
|
|
||||||
my $mclass = $tclass;
|
|
||||||
$mclass =~ s:\.client\.:.data.:g;
|
|
||||||
$mclass =~ s:Listener:Marshaller:g;
|
|
||||||
$mclass =~ s:Service:Marshaller:g;
|
|
||||||
$imports{$mclass} = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Strips the package name from the method argument types.
|
|
||||||
#
|
|
||||||
sub trim_args {
|
|
||||||
my @targs;
|
|
||||||
my $arg;
|
|
||||||
foreach $arg (@_) {
|
|
||||||
$arg =~ s/^.*\.([^.]+)$/$1/;
|
|
||||||
push @targs, $arg;
|
|
||||||
}
|
|
||||||
return @targs;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts a mixed case name to uppercase with underscores.
|
|
||||||
#
|
|
||||||
sub to_caps {
|
|
||||||
my ($fcode) = @_;
|
|
||||||
$fcode =~ s/([a-z])([A-Z])/$1_$2/g;
|
|
||||||
$fcode =~ tr/a-z/A-Z/;
|
|
||||||
return $fcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generates an argument list with argument names from a list of argument
|
|
||||||
# types.
|
|
||||||
#
|
|
||||||
sub gen_arglist {
|
|
||||||
my $arg;
|
|
||||||
my $arglist = "";
|
|
||||||
my $i = 1;
|
|
||||||
foreach $arg (@_) {
|
|
||||||
$arglist .= ", " if (length($arglist) > 0);
|
|
||||||
$arglist .= "$arg arg$i";
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
return $arglist;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts the supplied argument type list into a list of values suitable
|
|
||||||
# for use in an Object array initializer (meaning object types are used as
|
|
||||||
# is and primitive types are wrapped).
|
|
||||||
#
|
|
||||||
sub wrap_arglist {
|
|
||||||
my $skip = shift @_;
|
|
||||||
my $argvals = "";
|
|
||||||
my $i = 0;
|
|
||||||
my $arg;
|
|
||||||
foreach $arg (@_) {
|
|
||||||
$i++;
|
|
||||||
next if ($i <= $skip);
|
|
||||||
$argvals .= ", " if (length($argvals) > 0);
|
|
||||||
if ($arg eq "boolean") {
|
|
||||||
$argvals .= "new Boolean(arg$i)";
|
|
||||||
} elsif ($arg eq "byte") {
|
|
||||||
$argvals .= "new Byte(arg$i)";
|
|
||||||
} elsif ($arg eq "short") {
|
|
||||||
$argvals .= "new Short(arg$i)";
|
|
||||||
} elsif ($arg eq "char") {
|
|
||||||
$argvals .= "new Char(arg$i)";
|
|
||||||
} elsif ($arg eq "int") {
|
|
||||||
$argvals .= "new Integer(arg$i)";
|
|
||||||
} elsif ($arg eq "long") {
|
|
||||||
$argvals .= "new Long(arg$i)";
|
|
||||||
} elsif ($arg eq "float") {
|
|
||||||
$argvals .= "new Float(arg$i)";
|
|
||||||
} elsif ($arg eq "double") {
|
|
||||||
$argvals .= "new Double(arg$i)";
|
|
||||||
} elsif ($arg =~ /Listener$/) {
|
|
||||||
$argvals .= "listener$i";
|
|
||||||
} else {
|
|
||||||
$argvals .= "arg$i";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $argvals;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts the supplied argument type list into a list of values which
|
|
||||||
# represent the unwrapped value of each element.
|
|
||||||
#
|
|
||||||
sub unwrap_arglist {
|
|
||||||
my $argvals = "";
|
|
||||||
my $i = 0;
|
|
||||||
my $arg;
|
|
||||||
foreach $arg (@_) {
|
|
||||||
$argvals .= ", " if (length($argvals) > 0);
|
|
||||||
if ($arg eq "boolean") {
|
|
||||||
$argvals .= "((Boolean)args[$i]).booleanValue()";
|
|
||||||
} elsif ($arg eq "byte") {
|
|
||||||
$argvals .= "((Byte)args[$i]).byteValue()";
|
|
||||||
} elsif ($arg eq "short") {
|
|
||||||
$argvals .= "((Short)args[$i]).shortValue()";
|
|
||||||
} elsif ($arg eq "char") {
|
|
||||||
$argvals .= "((Char)args[$i]).charValue()";
|
|
||||||
} elsif ($arg eq "int") {
|
|
||||||
$argvals .= "((Integer)args[$i]).intValue()";
|
|
||||||
} elsif ($arg eq "long") {
|
|
||||||
$argvals .= "((Long)args[$i]).longValue()";
|
|
||||||
} elsif ($arg eq "float") {
|
|
||||||
$argvals .= "((Float)args[$i]).floatValue()";
|
|
||||||
} elsif ($arg eq "double") {
|
|
||||||
$argvals .= "((Double)args[$i]).doubleValue()";
|
|
||||||
} else {
|
|
||||||
$argvals .= "($arg)args[$i]";
|
|
||||||
}
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
return $argvals;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generates the necessary data to wrap listeners in listener marshaller
|
|
||||||
# instances.
|
|
||||||
#
|
|
||||||
sub gen_listener_args {
|
|
||||||
my $arg;
|
|
||||||
my @listener_args = ();
|
|
||||||
my $i = 0;
|
|
||||||
foreach $arg (@_) {
|
|
||||||
$i++;
|
|
||||||
next unless ($arg =~ /Listener$/);
|
|
||||||
|
|
||||||
my $mtype = $arg;
|
|
||||||
if ($mtype eq "InvocationListener") {
|
|
||||||
$mtype = "ListenerMarshaller";
|
|
||||||
} else {
|
|
||||||
$mtype =~ s:Listener$:Marshaller:;
|
|
||||||
}
|
|
||||||
|
|
||||||
push (@listener_args, { "mtype" => $mtype,
|
|
||||||
"aname" => "listener$i",
|
|
||||||
"oaname" => "arg$i" });
|
|
||||||
}
|
|
||||||
return \@listener_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# If the specified file exists, it is opened and its RCS Id string is read
|
|
||||||
# and returned.
|
|
||||||
#
|
|
||||||
sub slurp_idstr {
|
|
||||||
my ($path) = @_;
|
|
||||||
my $idstr = "\$Id\$";
|
|
||||||
if (-f $path && open(IN, "$path")) {
|
|
||||||
while (<IN>) {
|
|
||||||
if (/(\$[I][d]: .*\$)/) {
|
|
||||||
$idstr = $1;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(IN);
|
|
||||||
}
|
|
||||||
return $idstr;
|
|
||||||
}
|
|
||||||
@@ -3,40 +3,21 @@
|
|||||||
|
|
||||||
package com.threerings.presents.tools;
|
package com.threerings.presents.tools;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
import org.apache.tools.ant.types.FileSet;
|
|
||||||
|
|
||||||
import org.apache.velocity.VelocityContext;
|
import org.apache.velocity.VelocityContext;
|
||||||
import org.apache.velocity.app.VelocityEngine;
|
|
||||||
|
|
||||||
import com.samskivert.util.CollectionUtil;
|
import com.samskivert.util.CollectionUtil;
|
||||||
import com.samskivert.util.ObjectUtil;
|
|
||||||
import com.samskivert.util.SortableArrayList;
|
import com.samskivert.util.SortableArrayList;
|
||||||
import com.samskivert.util.StringUtil;
|
import com.samskivert.util.StringUtil;
|
||||||
import com.samskivert.velocity.VelocityUtil;
|
|
||||||
|
|
||||||
import com.threerings.presents.client.Client;
|
import com.threerings.presents.client.Client;
|
||||||
import com.threerings.presents.client.InvocationService.InvocationListener;
|
import com.threerings.presents.client.InvocationService.InvocationListener;
|
||||||
@@ -50,186 +31,8 @@ import com.threerings.presents.server.InvocationException;
|
|||||||
* An Ant task for generating invocation service marshalling and
|
* An Ant task for generating invocation service marshalling and
|
||||||
* unmarshalling classes.
|
* unmarshalling classes.
|
||||||
*/
|
*/
|
||||||
public class GenServiceTask extends Task
|
public class GenServiceTask extends InvocationTask
|
||||||
{
|
{
|
||||||
/** Used to keep track of invocation service method listener arguments. */
|
|
||||||
public static class ListenerArgument
|
|
||||||
{
|
|
||||||
public int index;
|
|
||||||
|
|
||||||
public Class listener;
|
|
||||||
|
|
||||||
public ListenerArgument (int index, Class listener)
|
|
||||||
{
|
|
||||||
this.index = index+1;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMarshaller ()
|
|
||||||
{
|
|
||||||
String name = simpleName(listener);
|
|
||||||
// handle ye olde special case
|
|
||||||
if (name.equals("InvocationService.InvocationListener")) {
|
|
||||||
return "ListenerMarshaller";
|
|
||||||
}
|
|
||||||
name = StringUtil.replace(name, "Service", "Marshaller");
|
|
||||||
return StringUtil.replace(name, "Listener", "Marshaller");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to keep track of invocation service methods. */
|
|
||||||
public static class ServiceMethod
|
|
||||||
{
|
|
||||||
public Method method;
|
|
||||||
|
|
||||||
public ArrayList listenerArgs = new ArrayList();
|
|
||||||
|
|
||||||
public ServiceMethod (Class service, Method method, HashMap imports)
|
|
||||||
{
|
|
||||||
this.method = method;
|
|
||||||
|
|
||||||
// we need to look through our arguments and note any needed
|
|
||||||
// imports in the supplied table
|
|
||||||
Class[] args = method.getParameterTypes();
|
|
||||||
for (int ii = 0; ii < args.length; ii++) {
|
|
||||||
Class arg = args[ii];
|
|
||||||
while (arg.isArray()) {
|
|
||||||
arg = arg.getComponentType();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a listener, we need to add a listener
|
|
||||||
// argument info for it
|
|
||||||
if (InvocationListener.class.isAssignableFrom(arg)) {
|
|
||||||
listenerArgs.add(new ListenerArgument(ii, arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it's not primitive, global or in our package, we
|
|
||||||
// should add an import statement for it
|
|
||||||
if (arg.isPrimitive() ||
|
|
||||||
arg.getName().startsWith("java.lang") ||
|
|
||||||
ObjectUtil.equals(
|
|
||||||
arg.getPackage(), service.getPackage())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
imports.put(importify(arg.getName()), Boolean.TRUE);
|
|
||||||
|
|
||||||
// if it's a listener and not one of the special
|
|
||||||
// InvocationService listeners, we need to import its
|
|
||||||
// marshaller as well
|
|
||||||
if (InvocationListener.class.isAssignableFrom(arg) &&
|
|
||||||
!simpleName(arg).startsWith("InvocationService")) {
|
|
||||||
String mname = arg.getName();
|
|
||||||
mname = StringUtil.replace(mname, "Service", "Marshaller");
|
|
||||||
mname = StringUtil.replace(mname, "Listener", "Marshaller");
|
|
||||||
mname = StringUtil.replace(mname, ".client.", ".data.");
|
|
||||||
imports.put(importify(mname), Boolean.TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCode ()
|
|
||||||
{
|
|
||||||
return StringUtil.unStudlyName(method.getName()).toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getArgList ()
|
|
||||||
{
|
|
||||||
StringBuffer buf = new StringBuffer();
|
|
||||||
Class[] args = method.getParameterTypes();
|
|
||||||
for (int ii = 0; ii < args.length; ii++) {
|
|
||||||
if (buf.length() > 0) {
|
|
||||||
buf.append(", ");
|
|
||||||
}
|
|
||||||
buf.append(simpleName(args[ii])).append(" arg").append(ii+1);
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWrappedArgList (boolean skipFirst)
|
|
||||||
{
|
|
||||||
StringBuffer buf = new StringBuffer();
|
|
||||||
Class[] args = method.getParameterTypes();
|
|
||||||
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
|
|
||||||
if (buf.length() > 0) {
|
|
||||||
buf.append(", ");
|
|
||||||
}
|
|
||||||
buf.append(wrapArgument(args[ii], ii+1));
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasArgs ()
|
|
||||||
{
|
|
||||||
return (method.getParameterTypes().length > 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUnwrappedArgList (boolean listenerMode)
|
|
||||||
{
|
|
||||||
StringBuffer buf = new StringBuffer();
|
|
||||||
Class[] args = method.getParameterTypes();
|
|
||||||
for (int ii = (listenerMode ? 0 : 1); ii < args.length; ii++) {
|
|
||||||
if (buf.length() > 0) {
|
|
||||||
buf.append(", ");
|
|
||||||
}
|
|
||||||
buf.append(unwrapArgument(args[ii], listenerMode ? ii : ii-1,
|
|
||||||
listenerMode));
|
|
||||||
}
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String wrapArgument (Class clazz, int index)
|
|
||||||
{
|
|
||||||
if (clazz == Boolean.TYPE) {
|
|
||||||
return "new Boolean(arg" + index + ")";
|
|
||||||
} else if (clazz == Byte.TYPE) {
|
|
||||||
return "new Byte(arg" + index + ")";
|
|
||||||
} else if (clazz == Character.TYPE) {
|
|
||||||
return "new Character(arg" + index + ")";
|
|
||||||
} else if (clazz == Short.TYPE) {
|
|
||||||
return "new Short(arg" + index + ")";
|
|
||||||
} else if (clazz == Integer.TYPE) {
|
|
||||||
return "new Integer(arg" + index + ")";
|
|
||||||
} else if (clazz == Long.TYPE) {
|
|
||||||
return "new Long(arg" + index + ")";
|
|
||||||
} else if (clazz == Float.TYPE) {
|
|
||||||
return "new Float(arg" + index + ")";
|
|
||||||
} else if (clazz == Double.TYPE) {
|
|
||||||
return "new Double(arg" + index + ")";
|
|
||||||
} else if (InvocationListener.class.isAssignableFrom(clazz)) {
|
|
||||||
return "listener" + index;
|
|
||||||
} else {
|
|
||||||
return "arg" + index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String unwrapArgument (
|
|
||||||
Class clazz, int index, boolean listenerMode)
|
|
||||||
{
|
|
||||||
if (clazz == Boolean.TYPE) {
|
|
||||||
return "((Boolean)args[" + index + "]).booleanValue()";
|
|
||||||
} else if (clazz == Byte.TYPE) {
|
|
||||||
return "((Byte)args[" + index + "]).byteValue()";
|
|
||||||
} else if (clazz == Character.TYPE) {
|
|
||||||
return "((Character)args[" + index + "]).charValue()";
|
|
||||||
} else if (clazz == Short.TYPE) {
|
|
||||||
return "((Short)args[" + index + "]).shortValue()";
|
|
||||||
} else if (clazz == Integer.TYPE) {
|
|
||||||
return "((Integer)args[" + index + "]).intValue()";
|
|
||||||
} else if (clazz == Long.TYPE) {
|
|
||||||
return "((Long)args[" + index + "]).longValue()";
|
|
||||||
} else if (clazz == Float.TYPE) {
|
|
||||||
return "((Float)args[" + index + "]).floatValue()";
|
|
||||||
} else if (clazz == Double.TYPE) {
|
|
||||||
return "((Double)args[" + index + "]).doubleValue()";
|
|
||||||
} else if (listenerMode &&
|
|
||||||
InvocationListener.class.isAssignableFrom(clazz)) {
|
|
||||||
return "listener" + index;
|
|
||||||
} else {
|
|
||||||
return "(" + simpleName(clazz) + ")args[" + index + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to keep track of custom InvocationListener derivations. */
|
/** Used to keep track of custom InvocationListener derivations. */
|
||||||
public static class ServiceListener
|
public static class ServiceListener
|
||||||
{
|
{
|
||||||
@@ -261,107 +64,7 @@ public class GenServiceTask extends Task
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// documentation inherited
|
||||||
* Adds a nested <fileset> element which enumerates service
|
|
||||||
* declaration source files.
|
|
||||||
*/
|
|
||||||
public void addFileset (FileSet set)
|
|
||||||
{
|
|
||||||
_filesets.add(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures us with a header file that we'll prepend to all
|
|
||||||
* generated source files.
|
|
||||||
*/
|
|
||||||
public void setHeader (File header)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_header = IOUtils.toString(new FileReader(header));
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
System.err.println("Unabled to load header '" + header + ": " +
|
|
||||||
ioe.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the actual work of the task.
|
|
||||||
*/
|
|
||||||
public void execute () throws BuildException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_velocity = VelocityUtil.createEngine();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BuildException("Failure initializing Velocity", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList files = new ArrayList();
|
|
||||||
for (Iterator iter = _filesets.iterator(); iter.hasNext(); ) {
|
|
||||||
FileSet fs = (FileSet)iter.next();
|
|
||||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
||||||
File fromDir = fs.getDir(getProject());
|
|
||||||
String[] srcFiles = ds.getIncludedFiles();
|
|
||||||
for (int f = 0; f < srcFiles.length; f++) {
|
|
||||||
processService(new File(fromDir, srcFiles[f]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes an invocation service source file.
|
|
||||||
*/
|
|
||||||
protected void processService (File source)
|
|
||||||
{
|
|
||||||
// System.err.println("Processing " + source + "...");
|
|
||||||
// load up the file and determine it's package and classname
|
|
||||||
String pkgname = null, name = null;
|
|
||||||
try {
|
|
||||||
BufferedReader bin = new BufferedReader(new FileReader(source));
|
|
||||||
String line;
|
|
||||||
while ((line = bin.readLine()) != null) {
|
|
||||||
Matcher pm = PACKAGE_PATTERN.matcher(line);
|
|
||||||
if (pm.find()) {
|
|
||||||
pkgname = pm.group(1);
|
|
||||||
}
|
|
||||||
Matcher nm = NAME_PATTERN.matcher(line);
|
|
||||||
if (nm.find()) {
|
|
||||||
name = nm.group(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bin.close();
|
|
||||||
|
|
||||||
// make sure we found something
|
|
||||||
if (name == null) {
|
|
||||||
System.err.println(
|
|
||||||
"Unable to locate interface name in " + source + ".");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepend the package name to get a name we can Class.forName()
|
|
||||||
if (pkgname != null) {
|
|
||||||
name = pkgname + "." + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(
|
|
||||||
"Failed to parse " + source + ": " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
processService(source, Class.forName(name));
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(
|
|
||||||
"Failed to load " + name + ": " + e.getMessage());
|
|
||||||
System.err.println("Make sure the classes for which you will " +
|
|
||||||
"be generating interfaces are");
|
|
||||||
System.err.println("in ant's classpath.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a resolved invocation service class instance.
|
|
||||||
*/
|
|
||||||
protected void processService (File source, Class service)
|
protected void processService (File source, Class service)
|
||||||
{
|
{
|
||||||
System.out.println("Processing " + service.getName() + "...");
|
System.out.println("Processing " + service.getName() + "...");
|
||||||
@@ -410,11 +113,6 @@ public class GenServiceTask extends Task
|
|||||||
methods.add(new ServiceMethod(service, m, imports));
|
methods.add(new ServiceMethod(service, m, imports));
|
||||||
}
|
}
|
||||||
|
|
||||||
// String dname = StringUtil.replace(sname, "Service", "Dispatcher");
|
|
||||||
// String dpackage = StringUtil.replace(spackage, ".client", ".server");
|
|
||||||
// String pname = StringUtil.replace(sname, "Service", "Provider");
|
|
||||||
// String ppackage = StringUtil.replace(spackage, ".client", ".server");
|
|
||||||
|
|
||||||
generateMarshaller(source, sname, spackage, methods, listeners,
|
generateMarshaller(source, sname, spackage, methods, listeners,
|
||||||
imports.keySet().iterator());
|
imports.keySet().iterator());
|
||||||
generateDispatcher(source, sname, spackage, methods,
|
generateDispatcher(source, sname, spackage, methods,
|
||||||
@@ -453,7 +151,6 @@ public class GenServiceTask extends Task
|
|||||||
mpath = StringUtil.replace(mpath, "Service", "Marshaller");
|
mpath = StringUtil.replace(mpath, "Service", "Marshaller");
|
||||||
mpath = StringUtil.replace(mpath, "/client/", "/data/");
|
mpath = StringUtil.replace(mpath, "/client/", "/data/");
|
||||||
|
|
||||||
System.out.println("Generating " + mname + "...");
|
|
||||||
writeFile(mpath, sw.toString());
|
writeFile(mpath, sw.toString());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -497,7 +194,6 @@ public class GenServiceTask extends Task
|
|||||||
mpath = StringUtil.replace(mpath, "Service", "Dispatcher");
|
mpath = StringUtil.replace(mpath, "Service", "Dispatcher");
|
||||||
mpath = StringUtil.replace(mpath, "/client/", "/server/");
|
mpath = StringUtil.replace(mpath, "/client/", "/server/");
|
||||||
|
|
||||||
System.out.println("Generating " + dname + "...");
|
|
||||||
writeFile(mpath, sw.toString());
|
writeFile(mpath, sw.toString());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -506,57 +202,6 @@ public class GenServiceTask extends Task
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeFile (String path, String data)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (_header != null) {
|
|
||||||
data = _header + data;
|
|
||||||
}
|
|
||||||
FileUtils.writeStringToFile(new File(path), data, "UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void checkedAdd (SortableArrayList list, String value)
|
|
||||||
{
|
|
||||||
if (!list.contains(value)) {
|
|
||||||
list.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String simpleName (Class clazz)
|
|
||||||
{
|
|
||||||
if (clazz.isArray()) {
|
|
||||||
return simpleName(clazz.getComponentType()) + "[]";
|
|
||||||
} else {
|
|
||||||
Package pkg = clazz.getPackage();
|
|
||||||
int offset = (pkg == null) ? 0 : pkg.getName().length()+1;
|
|
||||||
String name = clazz.getName().substring(offset);
|
|
||||||
return StringUtil.replace(name, "$", ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String importify (String name)
|
|
||||||
{
|
|
||||||
int didx = name.indexOf("$");
|
|
||||||
return (didx == -1) ? name : name.substring(0, didx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A list of filesets that contain tile images. */
|
|
||||||
protected ArrayList _filesets = new ArrayList();
|
|
||||||
|
|
||||||
/** A header to put on all generated source files. */
|
|
||||||
protected String _header;
|
|
||||||
|
|
||||||
/** Used to generate source files from templates. */
|
|
||||||
protected VelocityEngine _velocity;
|
|
||||||
|
|
||||||
/** A regular expression for matching the package declaration. */
|
|
||||||
protected static final Pattern PACKAGE_PATTERN =
|
|
||||||
Pattern.compile("^\\s*package\\s+(\\S+)\\W");
|
|
||||||
|
|
||||||
/** A regular expression for matching the interface declaration. */
|
|
||||||
protected static final Pattern NAME_PATTERN =
|
|
||||||
Pattern.compile("^\\s*public\\s+interface\\s+(\\S+)\\W");
|
|
||||||
|
|
||||||
/** Specifies the path to the marshaller template. */
|
/** Specifies the path to the marshaller template. */
|
||||||
protected static final String MARSHALLER_TMPL =
|
protected static final String MARSHALLER_TMPL =
|
||||||
"com/threerings/presents/tools/marshaller.tmpl";
|
"com/threerings/presents/tools/marshaller.tmpl";
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
package com.threerings.presents.tools;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import org.apache.tools.ant.BuildException;
|
||||||
|
import org.apache.tools.ant.DirectoryScanner;
|
||||||
|
import org.apache.tools.ant.Task;
|
||||||
|
import org.apache.tools.ant.types.FileSet;
|
||||||
|
import org.apache.tools.ant.types.Reference;
|
||||||
|
import org.apache.tools.ant.util.ClasspathUtils;
|
||||||
|
|
||||||
|
import org.apache.velocity.app.VelocityEngine;
|
||||||
|
|
||||||
|
import com.samskivert.util.ObjectUtil;
|
||||||
|
import com.samskivert.util.SortableArrayList;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
import com.samskivert.velocity.VelocityUtil;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.InvocationService.InvocationListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base Ant task for generating invocation service related marshalling
|
||||||
|
* and unmarshalling classes.
|
||||||
|
*/
|
||||||
|
public abstract class InvocationTask extends Task
|
||||||
|
{
|
||||||
|
/** Used to keep track of invocation service method listener arguments. */
|
||||||
|
public static class ListenerArgument
|
||||||
|
{
|
||||||
|
public int index;
|
||||||
|
|
||||||
|
public Class listener;
|
||||||
|
|
||||||
|
public ListenerArgument (int index, Class listener)
|
||||||
|
{
|
||||||
|
this.index = index+1;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarshaller ()
|
||||||
|
{
|
||||||
|
String name = simpleName(listener);
|
||||||
|
// handle ye olde special case
|
||||||
|
if (name.equals("InvocationService.InvocationListener")) {
|
||||||
|
return "ListenerMarshaller";
|
||||||
|
}
|
||||||
|
name = StringUtil.replace(name, "Service", "Marshaller");
|
||||||
|
return StringUtil.replace(name, "Listener", "Marshaller");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to keep track of invocation service methods. */
|
||||||
|
public static class ServiceMethod
|
||||||
|
{
|
||||||
|
public Method method;
|
||||||
|
|
||||||
|
public ArrayList listenerArgs = new ArrayList();
|
||||||
|
|
||||||
|
public ServiceMethod (Class service, Method method, HashMap imports)
|
||||||
|
{
|
||||||
|
this.method = method;
|
||||||
|
|
||||||
|
// we need to look through our arguments and note any needed
|
||||||
|
// imports in the supplied table
|
||||||
|
Class[] args = method.getParameterTypes();
|
||||||
|
for (int ii = 0; ii < args.length; ii++) {
|
||||||
|
Class arg = args[ii];
|
||||||
|
while (arg.isArray()) {
|
||||||
|
arg = arg.getComponentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a listener, we need to add a listener
|
||||||
|
// argument info for it
|
||||||
|
if (InvocationListener.class.isAssignableFrom(arg)) {
|
||||||
|
listenerArgs.add(new ListenerArgument(ii, arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's not primitive, global or in our package, we
|
||||||
|
// should add an import statement for it
|
||||||
|
if (arg.isPrimitive() ||
|
||||||
|
arg.getName().startsWith("java.lang") ||
|
||||||
|
ObjectUtil.equals(
|
||||||
|
arg.getPackage(), service.getPackage())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
imports.put(importify(arg.getName()), Boolean.TRUE);
|
||||||
|
|
||||||
|
// if it's a listener and not one of the special
|
||||||
|
// InvocationService listeners, we need to import its
|
||||||
|
// marshaller as well
|
||||||
|
if (InvocationListener.class.isAssignableFrom(arg) &&
|
||||||
|
!simpleName(arg).startsWith("InvocationService")) {
|
||||||
|
String mname = arg.getName();
|
||||||
|
mname = StringUtil.replace(mname, "Service", "Marshaller");
|
||||||
|
mname = StringUtil.replace(mname, "Listener", "Marshaller");
|
||||||
|
mname = StringUtil.replace(mname, ".client.", ".data.");
|
||||||
|
imports.put(importify(mname), Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode ()
|
||||||
|
{
|
||||||
|
return StringUtil.unStudlyName(method.getName()).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getArgList ()
|
||||||
|
{
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
Class[] args = method.getParameterTypes();
|
||||||
|
for (int ii = 0; ii < args.length; ii++) {
|
||||||
|
if (buf.length() > 0) {
|
||||||
|
buf.append(", ");
|
||||||
|
}
|
||||||
|
buf.append(simpleName(args[ii])).append(" arg").append(ii+1);
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWrappedArgList (boolean skipFirst)
|
||||||
|
{
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
Class[] args = method.getParameterTypes();
|
||||||
|
for (int ii = (skipFirst ? 1 : 0); ii < args.length; ii++) {
|
||||||
|
if (buf.length() > 0) {
|
||||||
|
buf.append(", ");
|
||||||
|
}
|
||||||
|
buf.append(wrapArgument(args[ii], ii+1));
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasArgs ()
|
||||||
|
{
|
||||||
|
return (method.getParameterTypes().length > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnwrappedArgList (boolean listenerMode)
|
||||||
|
{
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
Class[] args = method.getParameterTypes();
|
||||||
|
for (int ii = (listenerMode ? 0 : 1); ii < args.length; ii++) {
|
||||||
|
if (buf.length() > 0) {
|
||||||
|
buf.append(", ");
|
||||||
|
}
|
||||||
|
buf.append(unwrapArgument(args[ii], listenerMode ? ii : ii-1,
|
||||||
|
listenerMode));
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String wrapArgument (Class clazz, int index)
|
||||||
|
{
|
||||||
|
if (clazz == Boolean.TYPE) {
|
||||||
|
return "new Boolean(arg" + index + ")";
|
||||||
|
} else if (clazz == Byte.TYPE) {
|
||||||
|
return "new Byte(arg" + index + ")";
|
||||||
|
} else if (clazz == Character.TYPE) {
|
||||||
|
return "new Character(arg" + index + ")";
|
||||||
|
} else if (clazz == Short.TYPE) {
|
||||||
|
return "new Short(arg" + index + ")";
|
||||||
|
} else if (clazz == Integer.TYPE) {
|
||||||
|
return "new Integer(arg" + index + ")";
|
||||||
|
} else if (clazz == Long.TYPE) {
|
||||||
|
return "new Long(arg" + index + ")";
|
||||||
|
} else if (clazz == Float.TYPE) {
|
||||||
|
return "new Float(arg" + index + ")";
|
||||||
|
} else if (clazz == Double.TYPE) {
|
||||||
|
return "new Double(arg" + index + ")";
|
||||||
|
} else if (InvocationListener.class.isAssignableFrom(clazz)) {
|
||||||
|
return "listener" + index;
|
||||||
|
} else {
|
||||||
|
return "arg" + index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String unwrapArgument (
|
||||||
|
Class clazz, int index, boolean listenerMode)
|
||||||
|
{
|
||||||
|
if (clazz == Boolean.TYPE) {
|
||||||
|
return "((Boolean)args[" + index + "]).booleanValue()";
|
||||||
|
} else if (clazz == Byte.TYPE) {
|
||||||
|
return "((Byte)args[" + index + "]).byteValue()";
|
||||||
|
} else if (clazz == Character.TYPE) {
|
||||||
|
return "((Character)args[" + index + "]).charValue()";
|
||||||
|
} else if (clazz == Short.TYPE) {
|
||||||
|
return "((Short)args[" + index + "]).shortValue()";
|
||||||
|
} else if (clazz == Integer.TYPE) {
|
||||||
|
return "((Integer)args[" + index + "]).intValue()";
|
||||||
|
} else if (clazz == Long.TYPE) {
|
||||||
|
return "((Long)args[" + index + "]).longValue()";
|
||||||
|
} else if (clazz == Float.TYPE) {
|
||||||
|
return "((Float)args[" + index + "]).floatValue()";
|
||||||
|
} else if (clazz == Double.TYPE) {
|
||||||
|
return "((Double)args[" + index + "]).doubleValue()";
|
||||||
|
} else if (listenerMode &&
|
||||||
|
InvocationListener.class.isAssignableFrom(clazz)) {
|
||||||
|
return "listener" + index;
|
||||||
|
} else {
|
||||||
|
return "(" + simpleName(clazz) + ")args[" + index + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a nested <fileset> element which enumerates service
|
||||||
|
* declaration source files.
|
||||||
|
*/
|
||||||
|
public void addFileset (FileSet set)
|
||||||
|
{
|
||||||
|
_filesets.add(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures us with a header file that we'll prepend to all
|
||||||
|
* generated source files.
|
||||||
|
*/
|
||||||
|
public void setHeader (File header)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_header = IOUtils.toString(new FileReader(header));
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
System.err.println("Unabled to load header '" + header + ": " +
|
||||||
|
ioe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Configures our classpath which we'll use to load service classes. */
|
||||||
|
public void setClasspathref (Reference pathref)
|
||||||
|
{
|
||||||
|
_cloader = ClasspathUtils.getClassLoaderForPath(
|
||||||
|
getProject(), pathref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Performs the actual work of the task. */
|
||||||
|
public void execute () throws BuildException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_velocity = VelocityUtil.createEngine();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BuildException("Failure initializing Velocity", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList files = new ArrayList();
|
||||||
|
for (Iterator iter = _filesets.iterator(); iter.hasNext(); ) {
|
||||||
|
FileSet fs = (FileSet)iter.next();
|
||||||
|
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||||
|
File fromDir = fs.getDir(getProject());
|
||||||
|
String[] srcFiles = ds.getIncludedFiles();
|
||||||
|
for (int f = 0; f < srcFiles.length; f++) {
|
||||||
|
processService(new File(fromDir, srcFiles[f]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Processes an invocation service source file. */
|
||||||
|
protected void processService (File source)
|
||||||
|
{
|
||||||
|
// System.err.println("Processing " + source + "...");
|
||||||
|
// load up the file and determine it's package and classname
|
||||||
|
String pkgname = null, name = null;
|
||||||
|
try {
|
||||||
|
BufferedReader bin = new BufferedReader(new FileReader(source));
|
||||||
|
String line;
|
||||||
|
while ((line = bin.readLine()) != null) {
|
||||||
|
Matcher pm = PACKAGE_PATTERN.matcher(line);
|
||||||
|
if (pm.find()) {
|
||||||
|
pkgname = pm.group(1);
|
||||||
|
}
|
||||||
|
Matcher nm = NAME_PATTERN.matcher(line);
|
||||||
|
if (nm.find()) {
|
||||||
|
name = nm.group(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bin.close();
|
||||||
|
|
||||||
|
// make sure we found something
|
||||||
|
if (name == null) {
|
||||||
|
System.err.println(
|
||||||
|
"Unable to locate interface name in " + source + ".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend the package name to get a name we can Class.forName()
|
||||||
|
if (pkgname != null) {
|
||||||
|
name = pkgname + "." + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(
|
||||||
|
"Failed to parse " + source + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
processService(source, _cloader.loadClass(name));
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(
|
||||||
|
"Failed to load " + name + ": " + e.getMessage());
|
||||||
|
System.err.println("Make sure the classes for which you will " +
|
||||||
|
"be generating interfaces are");
|
||||||
|
System.err.println("in ant's classpath.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Processes a resolved invocation service class instance. */
|
||||||
|
protected abstract void processService (File source, Class service);
|
||||||
|
|
||||||
|
protected void writeFile (String path, String data)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (_header != null) {
|
||||||
|
data = _header + data;
|
||||||
|
}
|
||||||
|
FileUtils.writeStringToFile(new File(path), data, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void checkedAdd (SortableArrayList list, String value)
|
||||||
|
{
|
||||||
|
if (!list.contains(value)) {
|
||||||
|
list.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String simpleName (Class clazz)
|
||||||
|
{
|
||||||
|
if (clazz.isArray()) {
|
||||||
|
return simpleName(clazz.getComponentType()) + "[]";
|
||||||
|
} else {
|
||||||
|
Package pkg = clazz.getPackage();
|
||||||
|
int offset = (pkg == null) ? 0 : pkg.getName().length()+1;
|
||||||
|
String name = clazz.getName().substring(offset);
|
||||||
|
return StringUtil.replace(name, "$", ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String importify (String name)
|
||||||
|
{
|
||||||
|
int didx = name.indexOf("$");
|
||||||
|
return (didx == -1) ? name : name.substring(0, didx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A list of filesets that contain tile images. */
|
||||||
|
protected ArrayList _filesets = new ArrayList();
|
||||||
|
|
||||||
|
/** A header to put on all generated source files. */
|
||||||
|
protected String _header;
|
||||||
|
|
||||||
|
/** Used to do our own classpath business. */
|
||||||
|
protected ClassLoader _cloader;
|
||||||
|
|
||||||
|
/** Used to generate source files from templates. */
|
||||||
|
protected VelocityEngine _velocity;
|
||||||
|
|
||||||
|
/** A regular expression for matching the package declaration. */
|
||||||
|
protected static final Pattern PACKAGE_PATTERN =
|
||||||
|
Pattern.compile("^\\s*package\\s+(\\S+)\\W");
|
||||||
|
|
||||||
|
/** A regular expression for matching the interface declaration. */
|
||||||
|
protected static final Pattern NAME_PATTERN =
|
||||||
|
Pattern.compile("^\\s*public\\s+interface\\s+(\\S+)\\W");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user