Rewrote the InvocationReceiver generation process in Java as an Ant task. I had
previously redone the InvocationService and DObject generation tools but not the InvocationReceiver tool. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3913 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
-375
@@ -1,375 +0,0 @@
|
|||||||
#!/usr/bin/perl -w
|
|
||||||
#
|
|
||||||
# $Id: genreceiver,v 1.4 2004/10/13 19:29:12 andrzej Exp $
|
|
||||||
#
|
|
||||||
# This script is used to generate invocation receiver marshaller and
|
|
||||||
# unmarshaller classes based on receiver interface definitions.
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use Getopt::Long;
|
|
||||||
use Template;
|
|
||||||
use File::Basename;
|
|
||||||
use POSIX qw(strftime);
|
|
||||||
use MD5;
|
|
||||||
|
|
||||||
# use Dumpvalue;
|
|
||||||
|
|
||||||
my $usage = "Usage: $0 [--verbose] [--force] [--classpath classpath] " .
|
|
||||||
"[--sourcedir sourcedir] <sourcedir/foo/bar/FooReceiver.java> [...]\n";
|
|
||||||
|
|
||||||
# get our options
|
|
||||||
my $verbose;
|
|
||||||
my $force;
|
|
||||||
my $classpath;
|
|
||||||
my $sourcedir = ".";
|
|
||||||
GetOptions("verbose" => \$verbose,
|
|
||||||
"force" => \$force,
|
|
||||||
"classpath=s" => \$classpath,
|
|
||||||
"sourcedir=s" => \$sourcedir,
|
|
||||||
);
|
|
||||||
|
|
||||||
# make sure we have at least one source file argument
|
|
||||||
die $usage unless (@ARGV);
|
|
||||||
|
|
||||||
my $ISVC_CNAME = "com.threerings.presents.client.InvocationReceiver";
|
|
||||||
|
|
||||||
# locate a few necessary other files
|
|
||||||
my $script = basename($0);
|
|
||||||
my $rootdir = dirname($0); $rootdir =~ s:bin$:.:g;
|
|
||||||
my $sendtmpl = "$rootdir/lib/sender.tmpl";
|
|
||||||
my $dcdrtmpl = "$rootdir/lib/decoder.tmpl";
|
|
||||||
|
|
||||||
# now process our source files
|
|
||||||
my %imports;
|
|
||||||
my $srcfile;
|
|
||||||
my $spackage;
|
|
||||||
|
|
||||||
while ($srcfile = shift) {
|
|
||||||
# do a quick check to see if the sender file is newer than the
|
|
||||||
# receiver file in which case we can stop now
|
|
||||||
my $tpath = $srcfile;
|
|
||||||
$tpath =~ s/Receiver/Sender/;
|
|
||||||
$tpath =~ s:/client/:/server/:;
|
|
||||||
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 $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 InvocationReceiver, we
|
|
||||||
# don't want to do nothin'
|
|
||||||
die "$script: $ifcname does not extend " .
|
|
||||||
"InvocationReceiver\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);
|
|
||||||
|
|
||||||
# generate our "sender" method name
|
|
||||||
my $smethname = $methname;
|
|
||||||
$smethname =~ s:^received:send:;
|
|
||||||
|
|
||||||
# push the method onto the list
|
|
||||||
push @methods, {
|
|
||||||
"mname" => $methname,
|
|
||||||
"sname" => $smethname,
|
|
||||||
"mcode" => to_caps($methname),
|
|
||||||
"mcodeval" => $methcount++,
|
|
||||||
"arglist" => gen_arglist(@trimmed_args),
|
|
||||||
"wrapped_args" => wrap_arglist(0, @trimmed_args),
|
|
||||||
"unwrapped_args" => unwrap_arglist(@trimmed_args),
|
|
||||||
};
|
|
||||||
|
|
||||||
} 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 receiver name
|
|
||||||
my $svcname = substr($ifcname, length($ifcpkg)+1);
|
|
||||||
$svcname =~ s/Receiver$//g;
|
|
||||||
die "$script: Unable to discern receiver name from $ifcname.\n".
|
|
||||||
"Receiver interface definitions should be named <foo>Receiver.\n"
|
|
||||||
if ($svcname eq $ifcname);
|
|
||||||
|
|
||||||
# assume everything's in the same package to start
|
|
||||||
my $sendpkg = $ifcpkg;
|
|
||||||
my $dcdrpkg = $ifcpkg;
|
|
||||||
|
|
||||||
# if the interface is in a 'client' package, we'll put the sender in
|
|
||||||
# the associated 'server' package and leave the decoder in the
|
|
||||||
# 'client' package
|
|
||||||
if ($ifcpkg =~ m/\.client$/) {
|
|
||||||
$sendpkg =~ 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>Sender class definition
|
|
||||||
my $sendpath = $sendpkg;
|
|
||||||
$sendpath =~ s:\.:/:g;
|
|
||||||
$sendpath = "$sourcedir/$sendpath/$svcname" . "Sender.java";
|
|
||||||
|
|
||||||
print "$script: Generating sender:\n" .
|
|
||||||
" $sendpkg.${svcname}Sender in\n" .
|
|
||||||
" $sendpath\n" if ($verbose);
|
|
||||||
|
|
||||||
# populate a data table with all the data we'll need to fill out our
|
|
||||||
# sender template file
|
|
||||||
my %mimports = %imports;
|
|
||||||
$mimports{"com.threerings.presents.data.ClientObject"} = 1;
|
|
||||||
$mimports{"com.threerings.presents.server.InvocationSender"} = 1;
|
|
||||||
$mimports{"$dcdrpkg.${svcname}Decoder"} = 1;
|
|
||||||
my @mimportsl = sort keys %mimports;
|
|
||||||
my %mdata = ( "idstr" => slurp_idstr($sendpath),
|
|
||||||
"imports" => \@mimportsl,
|
|
||||||
"name" => $svcname,
|
|
||||||
"package" => $sendpkg,
|
|
||||||
"methods" => \@methods,
|
|
||||||
"genstamp" => $genstamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
# my $dumper = Dumpvalue->new;
|
|
||||||
# $dumper->dumpValue(\%mdata);
|
|
||||||
|
|
||||||
$tt->process($sendtmpl, \%mdata, $sendpath) or
|
|
||||||
die "$script: Unable to process template file '$sendtmpl': " .
|
|
||||||
$tt->error() . "\n";
|
|
||||||
|
|
||||||
# now we generate the <foo>Decoder class definition
|
|
||||||
my $dcdrpath = $dcdrpkg;
|
|
||||||
$dcdrpath =~ s:\.:/:g;
|
|
||||||
$dcdrpath = "$sourcedir/$dcdrpath/$svcname" . "Decoder.java";
|
|
||||||
|
|
||||||
print "$script: Generating decoder:\n" .
|
|
||||||
" $dcdrpkg.${svcname}Decoder in\n" .
|
|
||||||
" $dcdrpath\n" if ($verbose);
|
|
||||||
|
|
||||||
# populate a data table with all the data we'll need to fill out our
|
|
||||||
# decoder template file
|
|
||||||
my %dimports = %imports;
|
|
||||||
$dimports{"com.threerings.presents.client.InvocationDecoder"} = 1;
|
|
||||||
my @dimportsl = sort keys %dimports;
|
|
||||||
my %ddata = ( "idstr" => slurp_idstr($dcdrpath),
|
|
||||||
"imports" => \@dimportsl,
|
|
||||||
"name" => $svcname,
|
|
||||||
"receiver_code" => MD5->hexhash($ifcname),
|
|
||||||
"package" => $dcdrpkg,
|
|
||||||
"methods" => \@methods,
|
|
||||||
"genstamp" => $genstamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
# my $dumper = Dumpvalue->new;
|
|
||||||
# $dumper->dumpValue(\%ddata);
|
|
||||||
|
|
||||||
$tt->process($dcdrtmpl, \%ddata, $dcdrpath) or
|
|
||||||
die "$script: Unable to process template file '$dcdrtmpl': " .
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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)";
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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;
|
|
||||||
}
|
|
||||||
@@ -71,17 +71,25 @@
|
|||||||
</service>
|
</service>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- generates sender and decoder classes for all invocation
|
<!-- generates sender and decoder classes for all invocation -->
|
||||||
receiver declarations -->
|
<!-- receiver declarations -->
|
||||||
<target name="genreceiver">
|
<target name="genreceiver">
|
||||||
<apply executable="bin/genreceiver" failonerror="true" parallel="true">
|
<taskdef name="receiver"
|
||||||
<arg value="--classpath"/>
|
classname="com.threerings.presents.tools.GenReceiverTask"
|
||||||
<arg value="${classes.dir}"/>
|
classpathref="classpath"/>
|
||||||
<arg value="--sourcedir"/>
|
<!-- make sure the receiver class files are all compiled -->
|
||||||
<arg value="src/java"/>
|
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||||
|
debug="on" optimize="${build.optimize}" deprecation="on"
|
||||||
|
source="1.4" target="1.4">
|
||||||
|
<classpath refid="classpath"/>
|
||||||
|
<include name="**/*Receiver.java"/>
|
||||||
|
<exclude name="**/InvocationReceiver.java"/>
|
||||||
|
</javac>
|
||||||
|
<!-- now generate the associated files -->
|
||||||
|
<receiver header="lib/SOURCE_HEADER" classpathref="classpath">
|
||||||
<fileset dir="src/java" includes="**/*Receiver.java"
|
<fileset dir="src/java" includes="**/*Receiver.java"
|
||||||
excludes="**/InvocationReceiver.java"/>
|
excludes="**/InvocationReceiver.java"/>
|
||||||
</apply>
|
</receiver>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- checks the availability of certain libraries -->
|
<!-- checks the availability of certain libraries -->
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Narya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/narya/
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.presents.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.velocity.VelocityContext;
|
||||||
|
|
||||||
|
import com.samskivert.util.CollectionUtil;
|
||||||
|
import com.samskivert.util.SortableArrayList;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.threerings.presents.client.InvocationDecoder;
|
||||||
|
import com.threerings.presents.data.ClientObject;
|
||||||
|
import com.threerings.presents.server.InvocationSender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Ant task for generating invocation receiver marshalling and unmarshalling
|
||||||
|
* classes.
|
||||||
|
*/
|
||||||
|
public class GenReceiverTask extends InvocationTask
|
||||||
|
{
|
||||||
|
// documentation inherited
|
||||||
|
protected void processService (File source, Class receiver)
|
||||||
|
{
|
||||||
|
System.out.println("Processing " + receiver.getName() + "...");
|
||||||
|
String rname = receiver.getName();
|
||||||
|
String rpackage = "";
|
||||||
|
int didx = rname.lastIndexOf(".");
|
||||||
|
if (didx != -1) {
|
||||||
|
rpackage = rname.substring(0, didx);
|
||||||
|
rname = rname.substring(didx+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the receiver class name is as we expect it to be
|
||||||
|
if (!rname.endsWith("Receiver")) {
|
||||||
|
System.err.println("Cannot process '" + rname + "':");
|
||||||
|
System.err.println(
|
||||||
|
"Receiver classes must be named SomethingReceiver.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap imports = new HashMap();
|
||||||
|
SortableArrayList methods = new SortableArrayList();
|
||||||
|
|
||||||
|
// we need to import the receiver itself
|
||||||
|
imports.put(importify(receiver.getName()), Boolean.TRUE);
|
||||||
|
|
||||||
|
// look through and locate our receiver methods
|
||||||
|
Method[] methdecls = receiver.getDeclaredMethods();
|
||||||
|
for (int ii = 0; ii < methdecls.length; ii++) {
|
||||||
|
Method m = methdecls[ii];
|
||||||
|
// receiver interface methods must be public and abstract
|
||||||
|
if (!Modifier.isPublic(m.getModifiers()) &&
|
||||||
|
!Modifier.isAbstract(m.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
methods.add(new ServiceMethod(receiver, m, imports));
|
||||||
|
}
|
||||||
|
methods.sort();
|
||||||
|
|
||||||
|
generateSender(source, rname, rpackage, methods,
|
||||||
|
imports.keySet().iterator());
|
||||||
|
generateDecoder(source, rname, rpackage, methods,
|
||||||
|
imports.keySet().iterator());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateSender (File source, String rname, String rpackage,
|
||||||
|
List methods, Iterator imports)
|
||||||
|
{
|
||||||
|
String name = StringUtil.replace(rname, "Receiver", "");
|
||||||
|
String sname = StringUtil.replace(rname, "Receiver", "Sender");
|
||||||
|
String spackage = StringUtil.replace(rpackage, ".client", ".server");
|
||||||
|
|
||||||
|
// construct our imports list
|
||||||
|
SortableArrayList implist = new SortableArrayList();
|
||||||
|
CollectionUtil.addAll(implist, imports);
|
||||||
|
checkedAdd(implist, ClientObject.class.getName());
|
||||||
|
checkedAdd(implist, InvocationSender.class.getName());
|
||||||
|
String dname = StringUtil.replace(rname, "Receiver", "Decoder");
|
||||||
|
checkedAdd(implist, rpackage + "." + dname);
|
||||||
|
implist.sort();
|
||||||
|
|
||||||
|
VelocityContext ctx = new VelocityContext();
|
||||||
|
ctx.put("name", name);
|
||||||
|
ctx.put("package", spackage);
|
||||||
|
ctx.put("methods", methods);
|
||||||
|
ctx.put("imports", implist);
|
||||||
|
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
_velocity.mergeTemplate(SENDER_TMPL, "UTF-8", ctx, sw);
|
||||||
|
|
||||||
|
// determine the path to our sender file
|
||||||
|
String mpath = source.getPath();
|
||||||
|
mpath = StringUtil.replace(mpath, "Receiver", "Sender");
|
||||||
|
mpath = StringUtil.replace(mpath, "/client/", "/server/");
|
||||||
|
|
||||||
|
writeFile(mpath, sw.toString());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed processing template");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateDecoder (
|
||||||
|
File source, String rname, String rpackage, List methods,
|
||||||
|
Iterator imports)
|
||||||
|
{
|
||||||
|
String name = StringUtil.replace(rname, "Receiver", "");
|
||||||
|
String dname = StringUtil.replace(rname, "Receiver", "Decoder");
|
||||||
|
|
||||||
|
// construct our imports list
|
||||||
|
SortableArrayList implist = new SortableArrayList();
|
||||||
|
CollectionUtil.addAll(implist, imports);
|
||||||
|
checkedAdd(implist, InvocationDecoder.class.getName());
|
||||||
|
implist.sort();
|
||||||
|
|
||||||
|
VelocityContext ctx = new VelocityContext();
|
||||||
|
ctx.put("name", name);
|
||||||
|
ctx.put("receiver_code", StringUtil.md5hex(rpackage + "." + rname));
|
||||||
|
ctx.put("package", rpackage);
|
||||||
|
ctx.put("methods", methods);
|
||||||
|
ctx.put("imports", implist);
|
||||||
|
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
_velocity.mergeTemplate(DECODER_TMPL, "UTF-8", ctx, sw);
|
||||||
|
|
||||||
|
// determine the path to our sender file
|
||||||
|
String mpath = source.getPath();
|
||||||
|
mpath = StringUtil.replace(mpath, "Receiver", "Decoder");
|
||||||
|
|
||||||
|
writeFile(mpath, sw.toString());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed processing template");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Specifies the path to the sender template. */
|
||||||
|
protected static final String SENDER_TMPL =
|
||||||
|
"com/threerings/presents/tools/sender.tmpl";
|
||||||
|
|
||||||
|
/** Specifies the path to the decoder template. */
|
||||||
|
protected static final String DECODER_TMPL =
|
||||||
|
"com/threerings/presents/tools/decoder.tmpl";
|
||||||
|
}
|
||||||
@@ -119,6 +119,16 @@ public abstract class InvocationTask extends Task
|
|||||||
return StringUtil.unStudlyName(method.getName()).toUpperCase();
|
return StringUtil.unStudlyName(method.getName()).toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSenderMethodName ()
|
||||||
|
{
|
||||||
|
String mname = method.getName();
|
||||||
|
if (mname.startsWith("received")) {
|
||||||
|
return "send" + mname.substring("received".length());
|
||||||
|
} else {
|
||||||
|
return mname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getArgList (boolean providerMode)
|
public String getArgList (boolean providerMode)
|
||||||
{
|
{
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package $package;
|
||||||
|
|
||||||
|
#foreach ($import in $imports)
|
||||||
|
import $import;
|
||||||
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches calls to a {@link ${name}Receiver} instance.
|
||||||
|
*/
|
||||||
|
public class ${name}Decoder extends InvocationDecoder
|
||||||
|
{
|
||||||
|
/** The generated hash code used to identify this receiver class. */
|
||||||
|
public static final String RECEIVER_CODE = "$receiver_code";
|
||||||
|
|
||||||
|
#foreach ($m in $methods)
|
||||||
|
/** The method id used to dispatch {@link ${name}Receiver#$m.method.name}
|
||||||
|
* notifications. */
|
||||||
|
public static final int $m.code = $velocityCount;
|
||||||
|
|
||||||
|
#end
|
||||||
|
/**
|
||||||
|
* Creates a decoder that may be registered to dispatch invocation
|
||||||
|
* service notifications to the specified receiver.
|
||||||
|
*/
|
||||||
|
public ${name}Decoder (${name}Receiver receiver)
|
||||||
|
{
|
||||||
|
this.receiver = receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public String getReceiverCode ()
|
||||||
|
{
|
||||||
|
return RECEIVER_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public void dispatchNotification (int methodId, Object[] args)
|
||||||
|
{
|
||||||
|
switch (methodId) {
|
||||||
|
#foreach ($m in $methods)
|
||||||
|
case $m.code:
|
||||||
|
((${name}Receiver)receiver).${m.method.name}(
|
||||||
|
$m.getUnwrappedArgList(true)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
|
||||||
|
#end
|
||||||
|
default:
|
||||||
|
super.dispatchNotification(methodId, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package $package;
|
||||||
|
|
||||||
|
#foreach ($import in $imports)
|
||||||
|
import $import;
|
||||||
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to issue notifications to a {@link ${name}Receiver} instance on a
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
public class ${name}Sender extends InvocationSender
|
||||||
|
{
|
||||||
|
#foreach ($m in $methods)
|
||||||
|
/**
|
||||||
|
* Issues a notification that will result in a call to {@link
|
||||||
|
* ${name}Receiver#$m.method.name} on a client.
|
||||||
|
*/
|
||||||
|
public static void $m.senderMethodName (
|
||||||
|
ClientObject target, $m.getArgList(false))
|
||||||
|
{
|
||||||
|
sendNotification(
|
||||||
|
target, ${name}Decoder.RECEIVER_CODE, ${name}Decoder.$m.code,
|
||||||
|
new Object[] { $m.getWrappedArgList(false) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#end
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user