diff --git a/bin/gendobj b/bin/gendobj deleted file mode 100755 index 7bb6e0f9c..000000000 --- a/bin/gendobj +++ /dev/null @@ -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] \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 () { - 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 () { - # 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 ($_ !~ /{/) { - $_ = ; - 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 <$field 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 <$field 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 $field 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 - * $field 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 $field 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 <$field 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 - * $field 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 <$field 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 <indexth element of - * $field 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; -} diff --git a/build.xml b/build.xml index a3077330a..42dd9c3d6 100644 --- a/build.xml +++ b/build.xml @@ -21,15 +21,26 @@ - - - - - + + + + + + + + + + + + - + + - + diff --git a/src/java/com/threerings/presents/dobj/AttributeChangedEvent.java b/src/java/com/threerings/presents/dobj/AttributeChangedEvent.java index f87a1557f..8fc1a2b6e 100644 --- a/src/java/com/threerings/presents/dobj/AttributeChangedEvent.java +++ b/src/java/com/threerings/presents/dobj/AttributeChangedEvent.java @@ -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. Do not + * construct these objects by hand. 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) { diff --git a/src/java/com/threerings/presents/dobj/AttributesChangedEvent.java b/src/java/com/threerings/presents/dobj/AttributesChangedEvent.java deleted file mode 100644 index 31869e601..000000000 --- a/src/java/com/threerings/presents/dobj/AttributesChangedEvent.java +++ /dev/null @@ -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 attributes 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 names and values 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 indexth attribute that has - * changed. This value is not range checked, so be sure it is between - * 0 and getCount()-1. You may not receive - * an ArrayIndexOutOfBounds exception if you exceed - * getCount() but rather get a null name. - */ - public String getName (int index) - { - return _names[index]; - } - - /** - * Returns the new value of the indexth attribute. This value - * is not range checked, so be sure it is between 0 and - * getCount()-1. You may not receive an - * ArrayIndexOutOfBounds exception if you exceed - * getCount() but rather get a null value. - */ - public Object getValue (int index) - { - return _values[index]; - } - - /** - * Returns the new value of the indexth attribute as a - * short. This will fail if the attribute in question is not a short. - * The index parameter is not range checked, so be sure - * it is between 0 and getCount()-1. You may - * not receive an ArrayIndexOutOfBounds exception if you - * exceed getCount() but rather get a - * NullPointerException. - */ - public short getShortValue (int index) - { - return ((Short)_values[index]).shortValue(); - } - - /** - * Returns the new value of the indexth attribute as an - * int. This will fail if the attribute in question is not an int. - * The index parameter is not range checked, so be sure - * it is between 0 and getCount()-1. You may - * not receive an ArrayIndexOutOfBounds exception if you - * exceed getCount() but rather get a - * NullPointerException. - */ - public int getIntValue (int index) - { - return ((Integer)_values[index]).intValue(); - } - - /** - * Returns the new value of the indexth attribute as a - * long. This will fail if the attribute in question is not a long. - * The index parameter is not range checked, so be sure - * it is between 0 and getCount()-1. You may - * not receive an ArrayIndexOutOfBounds exception if you - * exceed getCount() but rather get a - * NullPointerException. - */ - public long getLongValue (int index) - { - return ((Long)_values[index]).longValue(); - } - - /** - * Returns the new value of the indexth attribute as a - * float. This will fail if the attribute in question is not a float. - * The index parameter is not range checked, so be sure - * it is between 0 and getCount()-1. You may - * not receive an ArrayIndexOutOfBounds exception if you - * exceed getCount() but rather get a - * NullPointerException. - */ - public float getFloatValue (int index) - { - return ((Float)_values[index]).floatValue(); - } - - /** - * Returns the new value of the indexth attribute as a - * double. This will fail if the attribute in question is not a - * double. The index parameter is not range checked, so - * be sure it is between 0 and getCount()-1. - * You may not receive an ArrayIndexOutOfBounds exception - * if you exceed getCount() but rather get a - * NullPointerException. - */ - 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; -} diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java index c79a9aa4f..c809e2c33 100644 --- a/src/java/com/threerings/presents/dobj/DObject.java +++ b/src/java/com/threerings/presents/dobj/DObject.java @@ -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 }; } diff --git a/src/java/com/threerings/presents/tools/GenDObjectTask.java b/src/java/com/threerings/presents/tools/GenDObjectTask.java new file mode 100644 index 000000000..b17ac440e --- /dev/null +++ b/src/java/com/threerings/presents/tools/GenDObjectTask.java @@ -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 indexth 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"; +} diff --git a/src/java/com/threerings/presents/tools/GenServiceTask.java b/src/java/com/threerings/presents/tools/GenServiceTask.java index 34ba53317..0ac5796ba 100644 --- a/src/java/com/threerings/presents/tools/GenServiceTask.java +++ b/src/java/com/threerings/presents/tools/GenServiceTask.java @@ -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)); } diff --git a/src/java/com/threerings/presents/tools/GenUtil.java b/src/java/com/threerings/presents/tools/GenUtil.java new file mode 100644 index 000000000..20cf9b0e0 --- /dev/null +++ b/src/java/com/threerings/presents/tools/GenUtil.java @@ -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 + * type[]). + */ + 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 int into + * an Integer 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 + * Integer object into an int. + */ + 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; + } +} diff --git a/src/java/com/threerings/presents/tools/InvocationTask.java b/src/java/com/threerings/presents/tools/InvocationTask.java index 7c02e21cd..92bd55563 100644 --- a/src/java/com/threerings/presents/tools/InvocationTask.java +++ b/src/java/com/threerings/presents/tools/InvocationTask.java @@ -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|$)"); } diff --git a/src/java/com/threerings/presents/tools/dobject_field.tmpl b/src/java/com/threerings/presents/tools/dobject_field.tmpl new file mode 100644 index 000000000..e40f99b62 --- /dev/null +++ b/src/java/com/threerings/presents/tools/dobject_field.tmpl @@ -0,0 +1,34 @@ + /** + * Requests that the $field 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 indexth element of + * $field 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 diff --git a/src/java/com/threerings/presents/tools/dobject_name.tmpl b/src/java/com/threerings/presents/tools/dobject_name.tmpl new file mode 100644 index 000000000..197cf2946 --- /dev/null +++ b/src/java/com/threerings/presents/tools/dobject_name.tmpl @@ -0,0 +1,2 @@ + /** The field name of the $field field. */ + public static final String $capfield = "$field"; diff --git a/src/java/com/threerings/presents/tools/dobject_oidlist.tmpl b/src/java/com/threerings/presents/tools/dobject_oidlist.tmpl new file mode 100644 index 000000000..b6eff6122 --- /dev/null +++ b/src/java/com/threerings/presents/tools/dobject_oidlist.tmpl @@ -0,0 +1,19 @@ + /** + * Requests that oid be added to the $field + * 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 oid be removed from the + * $field 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); + } diff --git a/src/java/com/threerings/presents/tools/dobject_set.tmpl b/src/java/com/threerings/presents/tools/dobject_set.tmpl new file mode 100644 index 000000000..6f59db43a --- /dev/null +++ b/src/java/com/threerings/presents/tools/dobject_set.tmpl @@ -0,0 +1,45 @@ + /** + * Requests that the specified entry be added to the + * $field 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 $field 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 + * $field 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 $field 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; + }