Merry Christmas to the server CPUs. It occurred to me that we could
accomplish our "previous value" support in the distributed object system without using reflection and could also avoid using reflection in the case where we have already applied the event on the server (which is generally the case on the server). Rather than hacking up the gendobj script, I took this opportunity also to rewrite the DObject generation script as an Ant task and in doing so, implemented another recent idea which is that we can just augment the FooObject.java file instead of having a separate .dobj and .java file. You'd think it was spring there's so much cleaning going on. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3284 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
-306
@@ -1,306 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# $Id: gendobj,v 1.16 2003/04/30 22:44:48 mdb Exp $
|
||||
#
|
||||
# gendobj is used to generate DObject source file definitons basded on
|
||||
# abbreviated declarations. Because DObject fields all have standard
|
||||
# setter methods which automatically generate events in the distributed
|
||||
# object system, it is convenient to generate these methods rather than
|
||||
# implement them by hand.
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
|
||||
my $usage = "Usage: $0 [--verbose] [--force] <dobj_decl_files>\n";
|
||||
|
||||
# get our options
|
||||
my $verbose;
|
||||
my $force;
|
||||
GetOptions("verbose" => \$verbose,
|
||||
"force" => \$force);
|
||||
|
||||
die $usage unless (@ARGV);
|
||||
|
||||
my $source;
|
||||
while ($source = shift) {
|
||||
# massage the source file name into the destination file name
|
||||
my $dest = $source;
|
||||
$dest =~ s/.dobj$/.java/;
|
||||
if ($dest !~ /.java$/) {
|
||||
warn "Unable to infer target file name from original: $source\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# if the destination file is newer than the source file, do nothing
|
||||
my $dmtime = (stat($dest))[9];
|
||||
my $smtime = (stat($source))[9];
|
||||
if (!$force && (defined $dmtime) && ($dmtime > $smtime)) {
|
||||
warn "Skipping $source as source is newer.\n" if ($verbose);
|
||||
next;
|
||||
}
|
||||
|
||||
# if the destination file already exists, slurp the existing CVS Id
|
||||
# information out of it so that we can slip that into the new one
|
||||
my $idstr;
|
||||
if (-f $dest && open(IN, "$dest")) {
|
||||
while (<IN>) {
|
||||
if (/(\$[I][d]: .*\$)/) {
|
||||
$idstr = $1;
|
||||
}
|
||||
}
|
||||
close(IN);
|
||||
}
|
||||
|
||||
if (!open(IN, "$source")) {
|
||||
warn "Unable to open $source for reading: $!\n";
|
||||
next;
|
||||
}
|
||||
|
||||
print "Processing $source...\n" if ($verbose);
|
||||
|
||||
my @preamble;
|
||||
my @classdecl;
|
||||
my @classbody;
|
||||
my @fields;
|
||||
my $mode = "preamble";
|
||||
|
||||
while (<IN>) {
|
||||
# swap in the parsed Id string if we have one
|
||||
$_ =~ s/(\$[I][d]: .*\$)/$idstr/ if (defined $idstr);
|
||||
|
||||
if ($mode eq "preamble") {
|
||||
# look to see if we're transitioning from the preamble to the
|
||||
# class declaration
|
||||
if (/^public class [A-Za-z]*/) {
|
||||
$mode = "classbody";
|
||||
|
||||
# keep adding to classdecl until we find a line that
|
||||
# contains a {
|
||||
push(@classdecl, $_);
|
||||
while ($_ !~ /{/) {
|
||||
$_ = <IN>;
|
||||
last if (! defined $_);
|
||||
push(@classdecl, $_);
|
||||
}
|
||||
|
||||
} else {
|
||||
push (@preamble, $_);
|
||||
}
|
||||
|
||||
} elsif ($mode eq "classbody") {
|
||||
# if we've reached a line with just a } at the beginning, we
|
||||
# stop reading the file
|
||||
last if (/^}\s*$/);
|
||||
|
||||
# everything goes into the body
|
||||
push (@classbody, $_);
|
||||
|
||||
# check to see if we're seeing a DObject field declaration
|
||||
if (/^\s*public ([A-Za-z]+(\[\])*) ([A-Za-z]+)( = .*)?;\s*$/) {
|
||||
print "Matched field [type=$1, field=$3]\n" if ($verbose);
|
||||
push (@fields, [$1, $3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(IN);
|
||||
|
||||
# generate the output file
|
||||
if (!open(OUT, ">$dest")) {
|
||||
warn "Unable to open $dest for writing: $!\n";
|
||||
next;
|
||||
}
|
||||
|
||||
print "Writing output to $dest...\n" if ($verbose);
|
||||
|
||||
print OUT join("", @preamble);
|
||||
print OUT join("", @classdecl);
|
||||
|
||||
my $field;
|
||||
foreach $field (@fields) {
|
||||
print_dobj_constant($field->[0], $field->[1]);
|
||||
}
|
||||
|
||||
print OUT join("", @classbody);
|
||||
|
||||
foreach $field (@fields) {
|
||||
print_dobj_setters($field->[0], $field->[1]);
|
||||
}
|
||||
|
||||
print OUT "}\n";
|
||||
|
||||
close(OUT);
|
||||
}
|
||||
|
||||
sub print_dobj_constant
|
||||
{
|
||||
my ($type, $field) = @_;
|
||||
|
||||
# convert mixed case to upper with underscores
|
||||
my $fcode = $field;
|
||||
$fcode =~ s/([a-z])([A-Z])/$1_$2/g;
|
||||
$fcode =~ tr/a-z/A-Z/;
|
||||
|
||||
print OUT <<EOF;
|
||||
/** The field name of the <code>$field</code> field. */
|
||||
public static final String $fcode = "$field";
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
sub print_dobj_setters
|
||||
{
|
||||
my ($type, $field) = @_;
|
||||
|
||||
# convert mixed case to upper with underscores
|
||||
my $fcode = $field;
|
||||
$fcode =~ s/([a-z])([A-Z])/$1_$2/g;
|
||||
$fcode =~ tr/a-z/A-Z/;
|
||||
|
||||
my $cfield = $field;
|
||||
$cfield =~ s/(\w)/\U$1/;
|
||||
|
||||
# some known types have special setters
|
||||
if ($type =~ ".*Set") {
|
||||
print OUT <<EOF;
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>$field</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void addTo$cfield (DSet.Entry elem)
|
||||
{
|
||||
requestEntryAdd($fcode, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>$field</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFrom$cfield (Comparable key)
|
||||
{
|
||||
requestEntryRemove($fcode, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>$field</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void update$cfield (DSet.Entry elem)
|
||||
{
|
||||
requestEntryUpdate($fcode, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>$field</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set value. The local value will be
|
||||
* updated immediately and an event will be propagated through the
|
||||
* system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
public void set$cfield ($type $field)
|
||||
{
|
||||
requestAttributeChange($fcode, $field);
|
||||
this.$field = $field;
|
||||
}
|
||||
EOF
|
||||
|
||||
} elsif ($type eq "OidList") {
|
||||
print OUT <<EOF;
|
||||
|
||||
/**
|
||||
* Requests that the specified oid be added to the
|
||||
* <code>$field</code> oid list. The list will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void addTo$cfield (int oid)
|
||||
{
|
||||
requestOidAdd($fcode, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified oid be removed from the
|
||||
* <code>$field</code> oid list. The list will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFrom$cfield (int oid)
|
||||
{
|
||||
requestOidRemove($fcode, oid);
|
||||
}
|
||||
EOF
|
||||
|
||||
} else {
|
||||
my $value = wrap($type, $field);
|
||||
print OUT <<EOF;
|
||||
|
||||
/**
|
||||
* Requests that the <code>$field</code> field be set to the specified
|
||||
* value. The local value will be updated immediately and an event
|
||||
* will be propagated through the system to notify all listeners that
|
||||
* the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void set$cfield ($type $field)
|
||||
{
|
||||
requestAttributeChange($fcode, $value);
|
||||
this.$field = $field;
|
||||
}
|
||||
EOF
|
||||
|
||||
# add an element setter if this field is an array
|
||||
if ($type =~ m:\[\]$:) {
|
||||
my $etype = $type;
|
||||
$etype =~ s:\[\]$::g;
|
||||
my $value = wrap($etype, "value");
|
||||
# this is perl; we're allowed to hack
|
||||
my $cfieldat = $cfield . "At";
|
||||
print OUT <<EOF;
|
||||
|
||||
/**
|
||||
* Requests that the <code>index</code>th element of
|
||||
* <code>$field</code> field be set to the specified value. The local
|
||||
* value will be updated immediately and an event will be propagated
|
||||
* through the system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
public void set$cfieldat ($etype value, int index)
|
||||
{
|
||||
requestElementUpdate($fcode, $value, index);
|
||||
this.$field\[index\] = value;
|
||||
}
|
||||
EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# we may need to wrap the field for certain types
|
||||
sub wrap
|
||||
{
|
||||
my ($type, $field) = @_;
|
||||
|
||||
if ($type eq "boolean") {
|
||||
return "new Boolean($field)";
|
||||
} elsif ($type eq "byte") {
|
||||
return "new Byte($field)";
|
||||
} elsif ($type eq "short") {
|
||||
return "new Short($field)";
|
||||
} elsif ($type eq "int") {
|
||||
return "new Integer($field)";
|
||||
} elsif ($type eq "long") {
|
||||
return "new Long($field)";
|
||||
} elsif ($type eq "float") {
|
||||
return "new Float($field)";
|
||||
} elsif ($type eq "double") {
|
||||
return "new Double($field)";
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
@@ -21,15 +21,26 @@
|
||||
<fileset dir="lib" includes="**/*.jar"/>
|
||||
</path>
|
||||
|
||||
<!-- generates .java files for all .dobj files -->
|
||||
<target name="gendobj">
|
||||
<apply executable="bin/gendobj" failonerror="true" parallel="true">
|
||||
<fileset dir="src/java" includes="**/*.dobj"/>
|
||||
</apply>
|
||||
<!-- generates additional methods for distributed object classes -->
|
||||
<target name="gendobj" depends="prepare">
|
||||
<taskdef name="dobj"
|
||||
classname="com.threerings.presents.tools.GenDObjectTask"
|
||||
classpathref="classpath"/>
|
||||
<!-- make sure the dobject class files are all compiled -->
|
||||
<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="**/*Object.java"/>
|
||||
</javac>
|
||||
<!-- now generate the associated files -->
|
||||
<dobj classpathref="classpath">
|
||||
<fileset dir="src/java" includes="**/*Object.java"/>
|
||||
</dobj>
|
||||
</target>
|
||||
|
||||
<!-- generates marshaller and dispatcher classes for all invocation
|
||||
service declarations -->
|
||||
<!-- generates marshaller and dispatcher classes for all invocation -->
|
||||
<!-- service declarations -->
|
||||
<target name="genservice">
|
||||
<taskdef name="service"
|
||||
classname="com.threerings.presents.tools.GenServiceTask"
|
||||
@@ -63,7 +74,7 @@
|
||||
</target>
|
||||
|
||||
<!-- prepares the application directories -->
|
||||
<target name="prepare" depends="gendobj">
|
||||
<target name="prepare">
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<mkdir dir="${classes.dir}"/>
|
||||
<mkdir dir="${classes.dir}/rsrc"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: AttributeChangedEvent.java,v 1.16 2004/08/27 02:20:20 mdb Exp $
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
@@ -32,26 +32,6 @@ import com.samskivert.util.StringUtil;
|
||||
*/
|
||||
public class AttributeChangedEvent extends NamedEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new attribute changed event on the specified target
|
||||
* object with the supplied attribute name and value.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param name the name of the attribute (data member) that has
|
||||
* changed.
|
||||
* @param value the new value of the attribute (in the case of
|
||||
* primitive types, the reflection-defined object-alternative is
|
||||
* used).
|
||||
*/
|
||||
public AttributeChangedEvent (int targetOid, String name,
|
||||
Object value, Object oldValue)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_value = value;
|
||||
_oldValue = oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
@@ -128,15 +108,41 @@ public class AttributeChangedEvent extends NamedEvent
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
// grab the previous value (if we're on the client)
|
||||
// if we have no old value, that means we're not running on the
|
||||
// master server and we have not already applied this attribute
|
||||
// change to the object, so we must grab the previous value and
|
||||
// actually apply the attribute change
|
||||
if (_oldValue == UNSET_OLD_VALUE) {
|
||||
_oldValue = target.getAttribute(_name);
|
||||
// pass the new value on to the object (this uses reflection
|
||||
// and is slow)
|
||||
target.setAttribute(_name, _value);
|
||||
}
|
||||
// pass the new value on to the object
|
||||
target.setAttribute(_name, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new attribute changed event on the specified target
|
||||
* object with the supplied attribute name and value. <em>Do not
|
||||
* construct these objects by hand.</em> Use {@link
|
||||
* DObject#changeAttribute} instead.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param name the name of the attribute (data member) that has
|
||||
* changed.
|
||||
* @param value the new value of the attribute (in the case of
|
||||
* primitive types, the reflection-defined object-alternative is
|
||||
* used).
|
||||
*/
|
||||
protected AttributeChangedEvent (
|
||||
int targetOid, String name, Object value, Object oldValue)
|
||||
{
|
||||
super(targetOid, name);
|
||||
_value = value;
|
||||
_oldValue = oldValue;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void notifyListener (Object listener)
|
||||
{
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
//
|
||||
// $Id: AttributesChangedEvent.java,v 1.12 2004/08/27 02:20:20 mdb Exp $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 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.dobj;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* An attribute<em>s</em> changed event is dispatched when multiple
|
||||
* attributes of a distributed object have been changed in a single
|
||||
* transaction.
|
||||
*
|
||||
* @see DObjectManager#postEvent
|
||||
*/
|
||||
public class AttributesChangedEvent extends DEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a new attribute changed event on the specified target
|
||||
* object with the supplied attribute name and value.
|
||||
*
|
||||
* @param targetOid the object id of the object whose attribute has
|
||||
* changed.
|
||||
* @param count the number of attributes that have changed (the length
|
||||
* of the <code>names</code> and <code>values</code> arrays need not
|
||||
* be exactly equal to the number of attributes changed, there can be
|
||||
* extra space at the end).
|
||||
* @param names the names of the attributes (data members) that have
|
||||
* changed.
|
||||
* @param values the new values of the attributes (in the case of
|
||||
* primitive types, the reflection-defined object-alternative is
|
||||
* used).
|
||||
*/
|
||||
public AttributesChangedEvent (int targetOid, int count,
|
||||
String[] names, Object[] values)
|
||||
{
|
||||
super(targetOid);
|
||||
_count = count;
|
||||
_names = names;
|
||||
_values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a blank instance of this event in preparation for
|
||||
* unserialization from the network.
|
||||
*/
|
||||
public AttributesChangedEvent ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of attributes that have changed.
|
||||
*/
|
||||
public int getCount ()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the index<em>th</em> attribute that has
|
||||
* changed. This value is not range checked, so be sure it is between
|
||||
* <code>0</code> and <code>getCount()-1</code>. You may not receive
|
||||
* an <code>ArrayIndexOutOfBounds</code> exception if you exceed
|
||||
* <code>getCount()</code> but rather get a null name.
|
||||
*/
|
||||
public String getName (int index)
|
||||
{
|
||||
return _names[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute. This value
|
||||
* is not range checked, so be sure it is between <code>0</code> and
|
||||
* <code>getCount()-1</code>. You may not receive an
|
||||
* <code>ArrayIndexOutOfBounds</code> exception if you exceed
|
||||
* <code>getCount()</code> but rather get a null value.
|
||||
*/
|
||||
public Object getValue (int index)
|
||||
{
|
||||
return _values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute as a
|
||||
* short. This will fail if the attribute in question is not a short.
|
||||
* The <code>index</code> parameter is not range checked, so be sure
|
||||
* it is between <code>0</code> and <code>getCount()-1</code>. You may
|
||||
* not receive an <code>ArrayIndexOutOfBounds</code> exception if you
|
||||
* exceed <code>getCount()</code> but rather get a
|
||||
* <code>NullPointerException</code>.
|
||||
*/
|
||||
public short getShortValue (int index)
|
||||
{
|
||||
return ((Short)_values[index]).shortValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute as an
|
||||
* int. This will fail if the attribute in question is not an int.
|
||||
* The <code>index</code> parameter is not range checked, so be sure
|
||||
* it is between <code>0</code> and <code>getCount()-1</code>. You may
|
||||
* not receive an <code>ArrayIndexOutOfBounds</code> exception if you
|
||||
* exceed <code>getCount()</code> but rather get a
|
||||
* <code>NullPointerException</code>.
|
||||
*/
|
||||
public int getIntValue (int index)
|
||||
{
|
||||
return ((Integer)_values[index]).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute as a
|
||||
* long. This will fail if the attribute in question is not a long.
|
||||
* The <code>index</code> parameter is not range checked, so be sure
|
||||
* it is between <code>0</code> and <code>getCount()-1</code>. You may
|
||||
* not receive an <code>ArrayIndexOutOfBounds</code> exception if you
|
||||
* exceed <code>getCount()</code> but rather get a
|
||||
* <code>NullPointerException</code>.
|
||||
*/
|
||||
public long getLongValue (int index)
|
||||
{
|
||||
return ((Long)_values[index]).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute as a
|
||||
* float. This will fail if the attribute in question is not a float.
|
||||
* The <code>index</code> parameter is not range checked, so be sure
|
||||
* it is between <code>0</code> and <code>getCount()-1</code>. You may
|
||||
* not receive an <code>ArrayIndexOutOfBounds</code> exception if you
|
||||
* exceed <code>getCount()</code> but rather get a
|
||||
* <code>NullPointerException</code>.
|
||||
*/
|
||||
public float getFloatValue (int index)
|
||||
{
|
||||
return ((Float)_values[index]).floatValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the new value of the index<em>th</em> attribute as a
|
||||
* double. This will fail if the attribute in question is not a
|
||||
* double. The <code>index</code> parameter is not range checked, so
|
||||
* be sure it is between <code>0</code> and <code>getCount()-1</code>.
|
||||
* You may not receive an <code>ArrayIndexOutOfBounds</code> exception
|
||||
* if you exceed <code>getCount()</code> but rather get a
|
||||
* <code>NullPointerException</code>.
|
||||
*/
|
||||
public double getDoubleValue (int index)
|
||||
{
|
||||
return ((Double)_values[index]).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this attribute change to the object.
|
||||
*/
|
||||
public boolean applyToObject (DObject target)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
// pass the new values on to the object
|
||||
for (int i = 0; i < _count; i++) {
|
||||
target.setAttribute(_names[i], _values[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
buf.append("CHANGES:");
|
||||
super.toString(buf);
|
||||
buf.append(", names=");
|
||||
StringUtil.toString(buf, _names);
|
||||
buf.append(", values=");
|
||||
StringUtil.toString(buf, _values);
|
||||
}
|
||||
|
||||
protected int _count;
|
||||
protected String[] _names;
|
||||
protected Object[] _values;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: DObject.java,v 1.79 2004/10/23 17:36:32 mdb Exp $
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
@@ -23,11 +23,14 @@ package com.threerings.presents.dobj;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.samskivert.util.ListUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
@@ -292,8 +295,13 @@ public class DObject extends TrackedObject
|
||||
*/
|
||||
public void addToSet (String setName, DSet.Entry entry)
|
||||
{
|
||||
getSet(setName); // validate the set
|
||||
requestEntryAdd(setName, entry);
|
||||
String mname = "addTo" + StringUtils.capitalize(setName);
|
||||
try {
|
||||
Method m = getClass().getMethod(mname, ENTRY_CLASS_ARGS);
|
||||
m.invoke(this, new Object[] { entry });
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("No such set: " + setName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,8 +309,13 @@ public class DObject extends TrackedObject
|
||||
*/
|
||||
public void updateSet (String setName, DSet.Entry entry)
|
||||
{
|
||||
getSet(setName); // validate the set
|
||||
requestEntryUpdate(setName, entry);
|
||||
String mname = "update" + StringUtils.capitalize(setName);
|
||||
try {
|
||||
Method m = getClass().getMethod(mname, ENTRY_CLASS_ARGS);
|
||||
m.invoke(this, new Object[] { entry });
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("No such set: " + setName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,8 +323,13 @@ public class DObject extends TrackedObject
|
||||
*/
|
||||
public void removeFromSet (String setName, Comparable key)
|
||||
{
|
||||
getSet(setName); // validate the set
|
||||
requestEntryRemove(setName, key);
|
||||
String mname = "removeFrom" + StringUtils.capitalize(setName);
|
||||
try {
|
||||
Method m = getClass().getMethod(mname, KEY_CLASS_ARGS);
|
||||
m.invoke(this, new Object[] { key });
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("No such set: " + setName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,6 +520,28 @@ public class DObject extends TrackedObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified attribute be changed to the specified
|
||||
* value. Normally the generated setter methods should be used but in
|
||||
* rare cases a caller may wish to update distributed fields in a
|
||||
* generic manner.
|
||||
*/
|
||||
public void changeAttribute (String name, Object value)
|
||||
throws ObjectAccessException
|
||||
{
|
||||
try {
|
||||
Field f = getField(name);
|
||||
requestAttributeChange(name, value, f.get(this));
|
||||
f.set(this, value);
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "changeAttribute() failure [name=" + name +
|
||||
", value=" + value +
|
||||
", vclass=" + value.getClass().getName() + "].";
|
||||
throw new ObjectAccessException(errmsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the named attribute to the specified value. This is only used
|
||||
* by the internals of the event dispatch mechanism and should not be
|
||||
@@ -512,39 +552,10 @@ public class DObject extends TrackedObject
|
||||
throws ObjectAccessException
|
||||
{
|
||||
try {
|
||||
// for values that contain other values (arrays and DSets), we
|
||||
// need to clone them before putting them in the object
|
||||
// because otherwise a subsequent event might come along and
|
||||
// modify these values before the networking thread has had a
|
||||
// chance to propagate this event to the clients
|
||||
|
||||
// i wish i could just call value.clone() but Object declares
|
||||
// clone() to be inaccessible, so we must cast the values to
|
||||
// their actual types to gain access to the widened clone()
|
||||
// methods
|
||||
if (value instanceof DSet) {
|
||||
value = ((DSet)value).clone();
|
||||
} else if (value instanceof int[]) {
|
||||
value = ((int[])value).clone();
|
||||
} else if (value instanceof String[]) {
|
||||
value = ((String[])value).clone();
|
||||
} else if (value instanceof byte[]) {
|
||||
value = ((byte[])value).clone();
|
||||
} else if (value instanceof long[]) {
|
||||
value = ((long[])value).clone();
|
||||
} else if (value instanceof float[]) {
|
||||
value = ((float[])value).clone();
|
||||
} else if (value instanceof short[]) {
|
||||
value = ((short[])value).clone();
|
||||
} else if (value instanceof double[]) {
|
||||
value = ((double[])value).clone();
|
||||
}
|
||||
|
||||
// now actually set the value
|
||||
getField(name).set(this, value);
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Attribute setting failure [name=" + name +
|
||||
String errmsg = "setAttribute() failure [name=" + name +
|
||||
", value=" + value +
|
||||
", vclass=" + value.getClass().getName() + "].";
|
||||
throw new ObjectAccessException(errmsg, e);
|
||||
@@ -564,7 +575,7 @@ public class DObject extends TrackedObject
|
||||
return getField(name).get(this);
|
||||
|
||||
} catch (Exception e) {
|
||||
String errmsg = "Attribute getting failure [name=" + name + "].";
|
||||
String errmsg = "getAttribute() failure [name=" + name + "].";
|
||||
throw new ObjectAccessException(errmsg, e);
|
||||
}
|
||||
}
|
||||
@@ -812,36 +823,23 @@ public class DObject extends TrackedObject
|
||||
* Called by derived instances when an attribute setter method was
|
||||
* called.
|
||||
*/
|
||||
protected void requestAttributeChange (String name, Object value)
|
||||
protected void requestAttributeChange (
|
||||
String name, Object value, Object oldValue)
|
||||
{
|
||||
try {
|
||||
// dispatch an attribute changed event
|
||||
postEvent(new AttributeChangedEvent(
|
||||
_oid, name, value, getAttribute(name)));
|
||||
|
||||
} catch (ObjectAccessException oae) {
|
||||
Log.warning("Unable to request attributeChange [name=" + name +
|
||||
", value=" + value + ", error=" + oae + "].");
|
||||
}
|
||||
// dispatch an attribute changed event
|
||||
postEvent(new AttributeChangedEvent(_oid, name, value, oldValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by derived instances when an element updater method was
|
||||
* called.
|
||||
*/
|
||||
protected void requestElementUpdate (String name, Object value, int index)
|
||||
protected void requestElementUpdate (
|
||||
String name, int index, Object value, Object oldValue)
|
||||
{
|
||||
try {
|
||||
// dispatch an attribute changed event
|
||||
Object oldValue = Array.get(getAttribute(name), index);
|
||||
postEvent(new ElementUpdatedEvent(
|
||||
_oid, name, value, oldValue, index));
|
||||
|
||||
} catch (ObjectAccessException oae) {
|
||||
Log.warning("Unable to request elementUpdate [name=" + name +
|
||||
", value=" + value + ", index=" + index +
|
||||
", error=" + oae + "].");
|
||||
}
|
||||
// dispatch an attribute changed event
|
||||
postEvent(new ElementUpdatedEvent(
|
||||
_oid, name, value, oldValue, index));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,72 +863,48 @@ public class DObject extends TrackedObject
|
||||
/**
|
||||
* Calls by derived instances when a set adder method was called.
|
||||
*/
|
||||
protected void requestEntryAdd (String name, DSet.Entry entry)
|
||||
protected void requestEntryAdd (String name, DSet set, DSet.Entry entry)
|
||||
{
|
||||
try {
|
||||
DSet set = (DSet)getAttribute(name);
|
||||
// if we're on the authoritative server, we update the set
|
||||
// immediately
|
||||
boolean alreadyApplied = false;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
if (!set.add(entry)) {
|
||||
Thread.dumpStack();
|
||||
}
|
||||
alreadyApplied = true;
|
||||
// if we're on the authoritative server, we update the set immediately
|
||||
boolean alreadyApplied = false;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
if (!set.add(entry)) {
|
||||
Thread.dumpStack();
|
||||
}
|
||||
// dispatch an entry added event
|
||||
postEvent(new EntryAddedEvent(_oid, name, entry, alreadyApplied));
|
||||
|
||||
} catch (ObjectAccessException oae) {
|
||||
Log.warning("Unable to request entryAdd [name=" + name +
|
||||
", entry=" + entry + ", error=" + oae + "].");
|
||||
alreadyApplied = true;
|
||||
}
|
||||
// dispatch an entry added event
|
||||
postEvent(new EntryAddedEvent(_oid, name, entry, alreadyApplied));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when a set remover method was called.
|
||||
*/
|
||||
protected void requestEntryRemove (String name, Comparable key)
|
||||
protected void requestEntryRemove (String name, DSet set, Comparable key)
|
||||
{
|
||||
try {
|
||||
DSet set = (DSet)getAttribute(name);
|
||||
// if we're on the authoritative server, we update the set
|
||||
// immediately
|
||||
DSet.Entry oldEntry = null;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
oldEntry = set.get(key);
|
||||
set.removeKey(key);
|
||||
}
|
||||
// dispatch an entry removed event
|
||||
postEvent(new EntryRemovedEvent(_oid, name, key, oldEntry));
|
||||
|
||||
} catch (ObjectAccessException oae) {
|
||||
Log.warning("Unable to request entryRemove [name=" + name +
|
||||
", key=" + key + ", error=" + oae + "].");
|
||||
// if we're on the authoritative server, we update the set immediately
|
||||
DSet.Entry oldEntry = null;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
oldEntry = set.get(key);
|
||||
set.removeKey(key);
|
||||
}
|
||||
// dispatch an entry removed event
|
||||
postEvent(new EntryRemovedEvent(_oid, name, key, oldEntry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls by derived instances when a set updater method was called.
|
||||
*/
|
||||
protected void requestEntryUpdate (String name, DSet.Entry entry)
|
||||
protected void requestEntryUpdate (String name, DSet set, DSet.Entry entry)
|
||||
{
|
||||
try {
|
||||
DSet set = (DSet)getAttribute(name);
|
||||
// if we're on the authoritative server, we update the set
|
||||
// immediately
|
||||
DSet.Entry oldEntry = null;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
oldEntry = set.get(entry.getKey());
|
||||
set.update(entry);
|
||||
}
|
||||
// dispatch an entry updated event
|
||||
postEvent(new EntryUpdatedEvent(_oid, name, entry, oldEntry));
|
||||
|
||||
} catch (ObjectAccessException oae) {
|
||||
Log.warning("Unable to request entryUpdate [name=" + name +
|
||||
", entry=" + entry + ", error=" + oae + "].");
|
||||
// if we're on the authoritative server, we update the set immediately
|
||||
DSet.Entry oldEntry = null;
|
||||
if (_omgr != null && _omgr.isManager(this)) {
|
||||
oldEntry = set.get(entry.getKey());
|
||||
set.update(entry);
|
||||
}
|
||||
// dispatch an entry updated event
|
||||
postEvent(new EntryUpdatedEvent(_oid, name, entry, oldEntry));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1004,4 +978,12 @@ public class DObject extends TrackedObject
|
||||
return ((Field)o1).getName().compareTo(((Field)o2).getName());
|
||||
}
|
||||
};
|
||||
|
||||
/** Used when calling set methods dynamically. */
|
||||
protected static final Class[] ENTRY_CLASS_ARGS =
|
||||
new Class[] { DSet.Entry.class };
|
||||
|
||||
/** Used when calling set methods dynamically. */
|
||||
protected static final Class[] KEY_CLASS_ARGS =
|
||||
new Class[] { Comparable.class };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 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.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
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.commons.lang.StringUtils;
|
||||
|
||||
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.VelocityContext;
|
||||
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.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
import com.threerings.presents.dobj.OidList;
|
||||
|
||||
/**
|
||||
* Generates necessary additional distributed object declarations and
|
||||
* methods.
|
||||
*/
|
||||
public class GenDObjectTask extends Task
|
||||
{
|
||||
/**
|
||||
* Adds a nested <fileset> element which enumerates service
|
||||
* declaration source files.
|
||||
*/
|
||||
public void addFileset (FileSet set)
|
||||
{
|
||||
_filesets.add(set);
|
||||
}
|
||||
|
||||
/** 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
|
||||
{
|
||||
if (_cloader == null) {
|
||||
String errmsg = "This task requires a 'classpathref' attribute " +
|
||||
"to be set to the project's classpath.";
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
|
||||
try {
|
||||
_velocity = VelocityUtil.createEngine();
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure initializing Velocity", e);
|
||||
}
|
||||
|
||||
// resolve the DObject class using our classloader
|
||||
try {
|
||||
_doclass = _cloader.loadClass(DObject.class.getName());
|
||||
_dsclass = _cloader.loadClass(DSet.class.getName());
|
||||
_olclass = _cloader.loadClass(OidList.class.getName());
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Can't resolve InvocationListener", 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++) {
|
||||
processObject(new File(fromDir, srcFiles[f]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Processes a distributed object source file. */
|
||||
protected void processObject (File source)
|
||||
{
|
||||
// System.err.println("Processing " + source + "...");
|
||||
// load up the file and determine it's package and classname
|
||||
String name = null;
|
||||
try {
|
||||
name = GenUtil.readClassName(source);
|
||||
} catch (Exception e) {
|
||||
System.err.println(
|
||||
"Failed to parse " + source + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
processObject(source, _cloader.loadClass(name));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
System.err.println(
|
||||
"Failed to load " + name + ".\n" +
|
||||
"Missing class: " + cnfe.getMessage());
|
||||
System.err.println(
|
||||
"Be sure to set the 'classpathref' attribute to a classpath\n" +
|
||||
"that contains your projects invocation service classes.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
/** Processes a resolved distributed object class instance. */
|
||||
protected void processObject (File source, Class oclass)
|
||||
{
|
||||
// make sure we extend distributed object
|
||||
if (!_doclass.isAssignableFrom(oclass) || _doclass.equals(oclass)) {
|
||||
// System.err.println("Skipping " + oclass.getName() + "...");
|
||||
return;
|
||||
}
|
||||
|
||||
// determine which fields we need to deal with
|
||||
ArrayList flist = new ArrayList();
|
||||
Field[] fields = oclass.getDeclaredFields();
|
||||
for (int ii = 0; ii < fields.length; ii++) {
|
||||
Field f = fields[ii];
|
||||
int mods = f.getModifiers();
|
||||
if (!Modifier.isPublic(mods) ||
|
||||
Modifier.isStatic(mods) ||
|
||||
Modifier.isTransient(mods)) {
|
||||
continue;
|
||||
}
|
||||
flist.add(f);
|
||||
}
|
||||
|
||||
// slurp our source file into newline separated strings
|
||||
String[] lines = null;
|
||||
try {
|
||||
BufferedReader bin = new BufferedReader(new FileReader(source));
|
||||
ArrayList llist = new ArrayList();
|
||||
String line = null;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
llist.add(line);
|
||||
}
|
||||
lines = (String[])llist.toArray(new String[llist.size()]);
|
||||
bin.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error reading '" + source + "': " + ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
// now determine where to insert our static field declarations and
|
||||
// our generated methods
|
||||
int bstart = -1, bend = -1;
|
||||
int nstart = -1, nend = -1;
|
||||
int mstart = -1, mend = -1;
|
||||
for (int ii = 0; ii < lines.length; ii++) {
|
||||
String line = lines[ii].trim();
|
||||
|
||||
// look for the start of the class body
|
||||
if (GenUtil.NAME_PATTERN.matcher(line).find()) {
|
||||
if (line.endsWith("{")) {
|
||||
bstart = ii+1;
|
||||
} else {
|
||||
// search down a few lines for the open brace
|
||||
for (int oo = 1; oo < 10; oo++) {
|
||||
if (get(lines, ii+oo).trim().endsWith("{")) {
|
||||
bstart = ii+oo+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// track the last } on a line by itself and we'll call that
|
||||
// the end of the class body
|
||||
} else if (line.equals("}")) {
|
||||
bend = ii;
|
||||
|
||||
// look for our field and method markers
|
||||
} else if (line.equals(FIELDS_START)) {
|
||||
nstart = ii;
|
||||
} else if (line.equals(FIELDS_END)) {
|
||||
nend = ii+1;
|
||||
} else if (line.equals(METHODS_START)) {
|
||||
mstart = ii;
|
||||
} else if (line.equals(METHODS_END)) {
|
||||
mend = ii+1;
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check the markers
|
||||
if (check(source, "fields start", nstart, "fields end", nend) ||
|
||||
check(source, "fields end", nend, "fields start", nstart) ||
|
||||
check(source, "methods start", mstart, "methods end", mend) ||
|
||||
check(source, "methods end", mend, "methods start", mstart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we have no previous markers then stuff the fields at the top of
|
||||
// the class body and the methods at the bottom
|
||||
if (nstart == -1) {
|
||||
nstart = bstart;
|
||||
nend = bstart;
|
||||
}
|
||||
if (mstart == -1) {
|
||||
mstart = bend;
|
||||
mend = bend;
|
||||
}
|
||||
|
||||
// generate our fields section and our methods section
|
||||
StringBuffer fsection = new StringBuffer();
|
||||
StringBuffer msection = new StringBuffer();
|
||||
for (int ii = 0; ii < flist.size(); ii++) {
|
||||
Field f = (Field)flist.get(ii);
|
||||
Class ftype = f.getType();
|
||||
String fname = f.getName();
|
||||
|
||||
// create our velocity context
|
||||
VelocityContext ctx = new VelocityContext();
|
||||
ctx.put("field", fname);
|
||||
ctx.put("type", GenUtil.simpleName(ftype));
|
||||
ctx.put("wrapfield", GenUtil.boxArgument(ftype, "value"));
|
||||
ctx.put("wrapofield", GenUtil.boxArgument(ftype, "ovalue"));
|
||||
ctx.put("capfield", StringUtil.unStudlyName(fname).toUpperCase());
|
||||
ctx.put("upfield", StringUtils.capitalize(fname));
|
||||
if (ftype.isArray()) {
|
||||
Class etype = ftype.getComponentType();
|
||||
ctx.put("elemtype", GenUtil.simpleName(etype));
|
||||
ctx.put("wrapelem", GenUtil.boxArgument(etype, "value"));
|
||||
ctx.put("wrapoelem", GenUtil.boxArgument(etype, "ovalue"));
|
||||
}
|
||||
|
||||
// now figure out which template to use
|
||||
String tname = "field.tmpl";
|
||||
if (_dsclass.isAssignableFrom(ftype)) {
|
||||
tname = "set.tmpl";
|
||||
} else if (_olclass.isAssignableFrom(ftype)) {
|
||||
tname = "oidlist.tmpl";
|
||||
}
|
||||
|
||||
// now generate our bits
|
||||
StringWriter fwriter = new StringWriter();
|
||||
StringWriter mwriter = new StringWriter();
|
||||
try {
|
||||
_velocity.mergeTemplate(NAME_TMPL, "UTF-8", ctx, fwriter);
|
||||
_velocity.mergeTemplate(
|
||||
BASE_TMPL + tname, "UTF-8", ctx, mwriter);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed processing template");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
// and append them as appropriate to the string buffers
|
||||
if (ii > 0) {
|
||||
fsection.append("\n");
|
||||
msection.append("\n");
|
||||
}
|
||||
fsection.append(fwriter.toString());
|
||||
msection.append(mwriter.toString());
|
||||
}
|
||||
|
||||
// now bolt everything back together into a class declaration
|
||||
try {
|
||||
BufferedWriter bout = new BufferedWriter(new FileWriter(source));
|
||||
for (int ii = 0; ii < nstart; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
if (fsection.length() > 0) {
|
||||
String prev = get(lines, nstart-1);
|
||||
if (!StringUtil.blank(prev) && !prev.equals("{")) {
|
||||
bout.newLine();
|
||||
}
|
||||
writeln(bout, " " + FIELDS_START);
|
||||
bout.write(fsection.toString());
|
||||
writeln(bout, " " + FIELDS_END);
|
||||
if (!StringUtil.blank(get(lines, nend))) {
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
for (int ii = nend; ii < mstart; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
|
||||
if (msection.length() > 0) {
|
||||
if (!StringUtil.blank(get(lines, mstart-1))) {
|
||||
bout.newLine();
|
||||
}
|
||||
writeln(bout, " " + METHODS_START);
|
||||
bout.write(msection.toString());
|
||||
writeln(bout, " " + METHODS_END);
|
||||
String next = get(lines, mend);
|
||||
if (!StringUtil.blank(next) && !next.equals("}")) {
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
for (int ii = mend; ii < lines.length; ii++) {
|
||||
writeln(bout, lines[ii]);
|
||||
}
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error writing to '" + source + "': " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/** Safely gets the <code>index</code>th line, returning the empty
|
||||
* string if we exceed the length of the array. */
|
||||
protected String get (String[] lines, int index)
|
||||
{
|
||||
return (index < lines.length) ? lines[index] : "";
|
||||
}
|
||||
|
||||
/** Helper function for sanity checking marker existence. */
|
||||
protected boolean check (File source, String mname, int mline,
|
||||
String fname, int fline)
|
||||
{
|
||||
if (mline == -1 && fline != -1) {
|
||||
System.err.println("Found " + fname + " marker (at line " +
|
||||
(fline+1) + ") but no " + mname +
|
||||
" marker in '" + source + "'.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Helper function for writing a string and a newline to a writer. */
|
||||
protected void writeln (BufferedWriter bout, String line)
|
||||
throws IOException
|
||||
{
|
||||
bout.write(line);
|
||||
bout.newLine();
|
||||
}
|
||||
|
||||
/** A list of filesets that contain tile images. */
|
||||
protected ArrayList _filesets = new ArrayList();
|
||||
|
||||
/** Used to do our own classpath business. */
|
||||
protected ClassLoader _cloader;
|
||||
|
||||
/** Used to generate source files from templates. */
|
||||
protected VelocityEngine _velocity;
|
||||
|
||||
/** {@link DObject} resolved with the proper classloader so that we
|
||||
* can compare it to loaded derived classes. */
|
||||
protected Class _doclass;
|
||||
|
||||
/** {@link DSet} resolved with the proper classloader so that we can
|
||||
* compare it to loaded derived classes. */
|
||||
protected Class _dsclass;
|
||||
|
||||
/** {@link OidList} resolved with the proper classloader so that we
|
||||
* can compare it to loaded derived classes. */
|
||||
protected Class _olclass;
|
||||
|
||||
/** Specifies the start of the path to our various templates. */
|
||||
protected static final String BASE_TMPL =
|
||||
"com/threerings/presents/tools/dobject_";
|
||||
|
||||
/** Specifies the path to the name code template. */
|
||||
protected static final String NAME_TMPL = BASE_TMPL + "name.tmpl";
|
||||
|
||||
// markers
|
||||
protected static final String MARKER = "// AUTO-GENERATED: ";
|
||||
protected static final String FIELDS_START = MARKER + "FIELDS START";
|
||||
protected static final String FIELDS_END = MARKER + "FIELDS END";
|
||||
protected static final String METHODS_START = MARKER + "METHODS START";
|
||||
protected static final String METHODS_END = MARKER + "METHODS END";
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class GenServiceTask extends InvocationTask
|
||||
|
||||
public String getName ()
|
||||
{
|
||||
String name = simpleName(listener);
|
||||
String name = GenUtil.simpleName(listener);
|
||||
name = StringUtil.replace(name, "Listener", "");
|
||||
int didx = name.indexOf(".");
|
||||
return name.substring(didx+1);
|
||||
@@ -118,7 +118,7 @@ public class GenServiceTask extends InvocationTask
|
||||
Class[] args = m.getParameterTypes();
|
||||
for (int aa = 0; aa < args.length; aa++) {
|
||||
if (_ilistener.isAssignableFrom(args[aa]) &&
|
||||
simpleName(args[aa]).startsWith(sname + ".")) {
|
||||
GenUtil.simpleName(args[aa]).startsWith(sname + ".")) {
|
||||
checkedAdd(listeners, new ServiceListener(
|
||||
service, args[aa], imports));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 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.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Utility methods used by our various source code generating tasks.
|
||||
*/
|
||||
public class GenUtil
|
||||
{
|
||||
/** A regular expression for matching the package declaration. */
|
||||
public static final Pattern PACKAGE_PATTERN =
|
||||
Pattern.compile("^\\s*package\\s+(\\S+)\\W");
|
||||
|
||||
/** A regular expression for matching the class or interface
|
||||
* declaration. */
|
||||
public static final Pattern NAME_PATTERN =
|
||||
Pattern.compile("^\\s*public\\s+(interface|class)\\s+(\\S+)(\\W|$)");
|
||||
|
||||
/**
|
||||
* Returns the name of the supplied class as it would likely appear in
|
||||
* code using the class (no package prefix, arrays specified as
|
||||
* <code>type[]</code>).
|
||||
*/
|
||||
public 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, "$", ".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Boxes" the supplied argument, ie. turning an <code>int</code> into
|
||||
* an <code>Integer</code> object.
|
||||
*/
|
||||
public static String boxArgument (Class clazz, String name)
|
||||
{
|
||||
if (clazz == Boolean.TYPE) {
|
||||
return "new Boolean(" + name + ")";
|
||||
} else if (clazz == Byte.TYPE) {
|
||||
return "new Byte(" + name + ")";
|
||||
} else if (clazz == Character.TYPE) {
|
||||
return "new Character(" + name + ")";
|
||||
} else if (clazz == Short.TYPE) {
|
||||
return "new Short(" + name + ")";
|
||||
} else if (clazz == Integer.TYPE) {
|
||||
return "new Integer(" + name + ")";
|
||||
} else if (clazz == Long.TYPE) {
|
||||
return "new Long(" + name + ")";
|
||||
} else if (clazz == Float.TYPE) {
|
||||
return "new Float(" + name + ")";
|
||||
} else if (clazz == Double.TYPE) {
|
||||
return "new Double(" + name + ")";
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Unboxes" the supplied argument, ie. turning an
|
||||
* <code>Integer</code> object into an <code>int</code>.
|
||||
*/
|
||||
public static String unboxArgument (Class clazz, String name)
|
||||
{
|
||||
if (clazz == Boolean.TYPE) {
|
||||
return "((Boolean)" + name + ").booleanValue()";
|
||||
} else if (clazz == Byte.TYPE) {
|
||||
return "((Byte)" + name + ").byteValue()";
|
||||
} else if (clazz == Character.TYPE) {
|
||||
return "((Character)" + name + ").charValue()";
|
||||
} else if (clazz == Short.TYPE) {
|
||||
return "((Short)" + name + ").shortValue()";
|
||||
} else if (clazz == Integer.TYPE) {
|
||||
return "((Integer)" + name + ").intValue()";
|
||||
} else if (clazz == Long.TYPE) {
|
||||
return "((Long)" + name + ").longValue()";
|
||||
} else if (clazz == Float.TYPE) {
|
||||
return "((Float)" + name + ").floatValue()";
|
||||
} else if (clazz == Double.TYPE) {
|
||||
return "((Double)" + name + ").doubleValue()";
|
||||
} else {
|
||||
return "(" + simpleName(clazz) + ")" + name + "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the supplied source file and locates the package and class
|
||||
* or interface name and returns a fully qualified class name.
|
||||
*/
|
||||
public static String readClassName (File source)
|
||||
throws IOException
|
||||
{
|
||||
// load up the file and determine it's package and classname
|
||||
String pkgname = null, name = null;
|
||||
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(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bin.close();
|
||||
|
||||
// make sure we found something
|
||||
if (name == null) {
|
||||
throw new IOException(
|
||||
"Unable to locate class or interface name in " + source + ".");
|
||||
}
|
||||
|
||||
// prepend the package name to get a name we can Class.forName()
|
||||
if (pkgname != null) {
|
||||
name = pkgname + "." + name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
package com.threerings.presents.tools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
@@ -15,9 +14,6 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@@ -58,7 +54,7 @@ public abstract class InvocationTask extends Task
|
||||
|
||||
public String getMarshaller ()
|
||||
{
|
||||
String name = simpleName(listener);
|
||||
String name = GenUtil.simpleName(listener);
|
||||
// handle ye olde special case
|
||||
if (name.equals("InvocationService.InvocationListener")) {
|
||||
return "ListenerMarshaller";
|
||||
@@ -108,7 +104,7 @@ public abstract class InvocationTask extends Task
|
||||
// InvocationService listeners, we need to import its
|
||||
// marshaller as well
|
||||
if (_ilistener.isAssignableFrom(arg) &&
|
||||
!simpleName(arg).startsWith("InvocationService")) {
|
||||
!GenUtil.simpleName(arg).startsWith("InvocationService")) {
|
||||
String mname = arg.getName();
|
||||
mname = StringUtil.replace(mname, "Service", "Marshaller");
|
||||
mname = StringUtil.replace(mname, "Listener", "Marshaller");
|
||||
@@ -131,7 +127,8 @@ public abstract class InvocationTask extends Task
|
||||
if (buf.length() > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append(simpleName(args[ii])).append(" arg").append(ii+1);
|
||||
buf.append(GenUtil.simpleName(args[ii]));
|
||||
buf.append(" arg").append(ii+1);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -144,7 +141,7 @@ public abstract class InvocationTask extends Task
|
||||
if (buf.length() > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append(wrapArgument(args[ii], ii+1));
|
||||
buf.append(boxArgument(args[ii], ii+1));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
@@ -167,60 +164,28 @@ public abstract class InvocationTask extends Task
|
||||
if (buf.length() > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append(unwrapArgument(args[ii], listenerMode ? ii : ii-1,
|
||||
buf.append(unboxArgument(args[ii], listenerMode ? ii : ii-1,
|
||||
listenerMode));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected String wrapArgument (Class clazz, int index)
|
||||
protected String boxArgument (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 (_ilistener.isAssignableFrom(clazz)) {
|
||||
return "listener" + index;
|
||||
if (_ilistener.isAssignableFrom(clazz)) {
|
||||
return GenUtil.boxArgument(clazz, "listener" + index);
|
||||
} else {
|
||||
return "arg" + index;
|
||||
return GenUtil.boxArgument(clazz, "arg" + index);
|
||||
}
|
||||
}
|
||||
|
||||
protected String unwrapArgument (
|
||||
protected String unboxArgument (
|
||||
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 && _ilistener.isAssignableFrom(clazz)) {
|
||||
if (listenerMode && _ilistener.isAssignableFrom(clazz)) {
|
||||
return "listener" + index;
|
||||
} else {
|
||||
return "(" + simpleName(clazz) + ")args[" + index + "]";
|
||||
return GenUtil.unboxArgument(clazz, "args[" + index + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,35 +259,9 @@ public abstract class InvocationTask extends Task
|
||||
{
|
||||
// System.err.println("Processing " + source + "...");
|
||||
// load up the file and determine it's package and classname
|
||||
String pkgname = null, name = null;
|
||||
String 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;
|
||||
}
|
||||
|
||||
name = GenUtil.readClassName(source);
|
||||
} catch (Exception e) {
|
||||
System.err.println(
|
||||
"Failed to parse " + source + ": " + e.getMessage());
|
||||
@@ -361,18 +300,6 @@ public abstract class InvocationTask extends Task
|
||||
}
|
||||
}
|
||||
|
||||
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("$");
|
||||
@@ -394,12 +321,4 @@ public abstract class InvocationTask extends Task
|
||||
/** {@link InvocationListener} resolved with the proper classloader so
|
||||
* that we can compare it to loaded derived classes. */
|
||||
protected Class _ilistener;
|
||||
|
||||
/** 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|$)");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Requests that the <code>$field</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
public void set$upfield ($type value)
|
||||
{
|
||||
$type ovalue = this.$field;
|
||||
requestAttributeChange(
|
||||
$capfield, $wrapfield, $wrapofield);
|
||||
this.$field = value;
|
||||
}
|
||||
#if ($elemtype)
|
||||
|
||||
/**
|
||||
* Requests that the <code>index</code>th element of
|
||||
* <code>$field</code> field be set to the specified value.
|
||||
* The local value will be updated immediately and an event will be
|
||||
* propagated through the system to notify all listeners that the
|
||||
* attribute did change. Proxied copies of this object (on clients)
|
||||
* will apply the value change when they received the attribute
|
||||
* changed notification.
|
||||
*/
|
||||
public void set${upfield}At ($elemtype value, int index)
|
||||
{
|
||||
$elemtype ovalue = this.$field[index];
|
||||
requestElementUpdate(
|
||||
$capfield, index, $wrapelem, $wrapoelem);
|
||||
this.$field[index] = value;
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,2 @@
|
||||
/** The field name of the <code>$field</code> field. */
|
||||
public static final String $capfield = "$field";
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Requests that <code>oid</code> be added to the <code>$field</code>
|
||||
* oid list. The list will not change until the event is actually
|
||||
* propagated through the system.
|
||||
*/
|
||||
public void addTo$upfield (int oid)
|
||||
{
|
||||
requestOidAdd($capfield, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that <code>oid</code> be removed from the
|
||||
* <code>$field</code> oid list. The list will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFrom$upfield (int oid)
|
||||
{
|
||||
requestOidRemove($capfield, oid);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>$field</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void addTo$upfield (DSet.Entry elem)
|
||||
{
|
||||
requestEntryAdd($capfield, $field, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>$field</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
public void removeFrom$upfield (Comparable key)
|
||||
{
|
||||
requestEntryRemove($capfield, $field, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>$field</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
public void update$upfield (DSet.Entry elem)
|
||||
{
|
||||
requestEntryUpdate($capfield, $field, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>$field</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set value. The local value will be
|
||||
* updated immediately and an event will be propagated through the
|
||||
* system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
public void set$upfield ($type $field)
|
||||
{
|
||||
requestAttributeChange($capfield, $field, this.$field);
|
||||
this.$field = $field;
|
||||
}
|
||||
Reference in New Issue
Block a user