From 59aeb61fe3a6301cad797be0cd15fbc94eceef59 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 19 Mar 2002 01:10:03 +0000 Subject: [PATCH] Added ability to update individual elements of an array field. The objects have helper functions named setAt() for such a purpose. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1134 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- bin/gendobj | 72 +++++-- .../com/threerings/presents/dobj/DObject.java | 12 +- .../presents/dobj/ElementUpdateListener.java | 23 ++ .../presents/dobj/ElementUpdatedEvent.java | 203 ++++++++++++++++++ .../threerings/presents/server/DOMTest.java | 23 +- .../presents/server/TestObject.dobj | 4 +- .../presents/server/TestObject.java | 66 +++++- 7 files changed, 378 insertions(+), 25 deletions(-) create mode 100644 src/java/com/threerings/presents/dobj/ElementUpdateListener.java create mode 100644 src/java/com/threerings/presents/dobj/ElementUpdatedEvent.java diff --git a/bin/gendobj b/bin/gendobj index a02a8fdf9..dacee4e14 100755 --- a/bin/gendobj +++ b/bin/gendobj @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: gendobj,v 1.10 2002/03/18 23:21:25 mdb Exp $ +# $Id: gendobj,v 1.11 2002/03/19 01:10:02 mdb Exp $ # # gendobj is used to generate DObject source file definitons basded on # abbreviated declarations. Because DObject fields all have standard @@ -160,24 +160,6 @@ sub print_dobj_setters my $cfield = $field; $cfield =~ s/(\w)/\U$1/; - # we may need to wrap the field for certain types - my $dobjval = $field; - if ($type eq "boolean") { - $dobjval = "new Boolean($field)"; - } elsif ($type eq "byte") { - $dobjval = "new Byte($field)"; - } elsif ($type eq "short") { - $dobjval = "new Short($field)"; - } elsif ($type eq "int") { - $dobjval = "new Integer($field)"; - } elsif ($type eq "long") { - $dobjval = "new Long($field)"; - } elsif ($type eq "float") { - $dobjval = "new Float($field)"; - } elsif ($type eq "double") { - $dobjval = "new Double($field)"; - } - # some known types have special setters if ($type eq "DSet") { 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) + { + this.$field\[index\] = value; + requestElementUpdate($fcode, $value, index); + } +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/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java index 73bbc89b3..61da46613 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.40 2002/03/18 23:21:26 mdb Exp $ +// $Id: DObject.java,v 1.41 2002/03/19 01:10:02 mdb Exp $ package com.threerings.presents.dobj; @@ -558,6 +558,16 @@ public class DObject postEvent(new AttributeChangedEvent(_oid, name, value)); } + /** + * Called by derived instances when an element updater method was + * called. + */ + protected void requestElementUpdate (String name, Object value, int index) + { + // dispatch an attribute changed event + postEvent(new ElementUpdatedEvent(_oid, name, value, index)); + } + /** * Calls by derived instances when an oid adder method was called. */ diff --git a/src/java/com/threerings/presents/dobj/ElementUpdateListener.java b/src/java/com/threerings/presents/dobj/ElementUpdateListener.java new file mode 100644 index 000000000..b1b4b0548 --- /dev/null +++ b/src/java/com/threerings/presents/dobj/ElementUpdateListener.java @@ -0,0 +1,23 @@ +// +// $Id: ElementUpdateListener.java,v 1.1 2002/03/19 01:10:03 mdb Exp $ + +package com.threerings.presents.dobj; + +/** + * Implemented by entites which wish to hear about element updates that + * take place for a particular distributed object. + * + * @see DObject#addListener + */ +public interface ElementUpdateListener extends ChangeListener +{ + /** + * Called when an element updated event has been dispatched on an + * object. This will be called after the event has been + * applied to the object. So fetching the element during this call + * will provide the new value for the element. + * + * @param event The event that was dispatched on the object. + */ + public void elementUpdated (ElementUpdatedEvent event); +} diff --git a/src/java/com/threerings/presents/dobj/ElementUpdatedEvent.java b/src/java/com/threerings/presents/dobj/ElementUpdatedEvent.java new file mode 100644 index 000000000..215bd1225 --- /dev/null +++ b/src/java/com/threerings/presents/dobj/ElementUpdatedEvent.java @@ -0,0 +1,203 @@ +// +// $Id: ElementUpdatedEvent.java,v 1.1 2002/03/19 01:10:03 mdb Exp $ + +package com.threerings.presents.dobj; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; + +import com.threerings.presents.Log; +import com.threerings.presents.io.ValueMarshaller; + +/** + * An element updated event is dispatched when an element of an array + * field in a distributed object is updated. It can also be constructed to + * request the update of an entry and posted to the dobjmgr. + * + * @see DObjectManager#postEvent + */ +public class ElementUpdatedEvent extends TypedEvent +{ + /** The typed object code for this event. */ + public static final short TYPE = TYPE_BASE + 11; + + /** + * Constructs a new element updated event on the specified target + * object with the supplied attribute name, element and index. + * + * @param targetOid the object id of the object whose attribute has + * changed. + * @param name the name of the attribute (data member) for which an + * element has changed. + * @param value the new value of the element (in the case of primitive + * types, the reflection-defined object-alternative is used). + * @param index the index in the array of the updated element. + */ + public ElementUpdatedEvent ( + int targetOid, String name, Object value, int index) + { + super(targetOid); + _name = name; + _value = value; + _index = index; + } + + /** + * Constructs a blank instance of this event in preparation for + * unserialization from the network. + */ + public ElementUpdatedEvent () + { + } + + /** + * Returns the name of the attribute that has changed. + */ + public String getName () + { + return _name; + } + + /** + * Returns the new value of the element. + */ + public Object getValue () + { + return _value; + } + + /** + * Returns the index of the element. + */ + public int getIndex () + { + return _index; + } + + /** + * Returns the new value of the element as a short. This will fail if + * the element in question is not a short. + */ + public short getShortValue () + { + return ((Short)_value).shortValue(); + } + + /** + * Returns the new value of the element as an int. This will fail if + * the element in question is not an int. + */ + public int getIntValue () + { + return ((Integer)_value).intValue(); + } + + /** + * Returns the new value of the element as a long. This will fail if + * the element in question is not a long. + */ + public long getLongValue () + { + return ((Long)_value).longValue(); + } + + /** + * Returns the new value of the element as a float. This will fail if + * the element in question is not a float. + */ + public float getFloatValue () + { + return ((Float)_value).floatValue(); + } + + /** + * Returns the new value of the element as a double. This will fail if + * the element in question is not a double. + */ + public double getDoubleValue () + { + return ((Double)_value).doubleValue(); + } + + /** + * Applies this element update to the object. + */ + public boolean applyToObject (DObject target) + throws ObjectAccessException + { + try { + // fetch the array field from the object + Field field = target.getClass().getField(_name); + Class ftype = field.getType(); + + // sanity check + if (!ftype.isArray()) { + String msg = "Requested to set element on non-array field."; + throw new Exception(msg); + } + + // we don't do any magical expansion or any funny business; + // the array should be big enough to contain the value being + // updated or we'll throw an ArrayIndexOutOfBoundsException + Array.set(field.get(target), _index, _value); + return true; + + } catch (Exception e) { + String msg = "Error updating element [field=" + _name + + ", index=" + _index + "]"; + throw new ObjectAccessException(msg, e); + } + } + + // documentation inherited + public short getType () + { + return TYPE; + } + + // documentation inherited + public void writeTo (DataOutputStream out) + throws IOException + { + super.writeTo(out); + out.writeUTF(_name); + out.writeInt(_index); + ValueMarshaller.writeTo(out, _value); + } + + // documentation inherited + public void readFrom (DataInputStream in) + throws IOException + { + super.readFrom(in); + _name = in.readUTF(); + _index = in.readInt(); + _value = ValueMarshaller.readFrom(in); + } + + // documentation inherited + protected void notifyListener (Object listener) + { + if (listener instanceof ElementUpdateListener) { + ((ElementUpdateListener)listener).elementUpdated(this); + } + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + buf.append("UPDATE:"); + super.toString(buf); + buf.append(", name=").append(_name); + buf.append(", value=").append(_value); + buf.append(", index=").append(_index); + } + + protected String _name; + protected Object _value; + protected int _index; +} diff --git a/tests/src/java/com/threerings/presents/server/DOMTest.java b/tests/src/java/com/threerings/presents/server/DOMTest.java index 3a24aee35..25adbaef6 100644 --- a/tests/src/java/com/threerings/presents/server/DOMTest.java +++ b/tests/src/java/com/threerings/presents/server/DOMTest.java @@ -1,11 +1,13 @@ // -// $Id: DOMTest.java,v 1.7 2002/02/09 07:50:04 mdb Exp $ +// $Id: DOMTest.java,v 1.8 2002/03/19 01:10:03 mdb Exp $ package com.threerings.presents.server; import junit.framework.Test; import junit.framework.TestCase; +import com.samskivert.util.StringUtil; + import com.threerings.presents.Log; import com.threerings.presents.dobj.*; @@ -13,7 +15,7 @@ import com.threerings.presents.dobj.*; * A simple test case for the dobjmgr. */ public class DOMTest extends TestCase - implements Subscriber, AttributeChangeListener + implements Subscriber, AttributeChangeListener, ElementUpdateListener { public DOMTest () { @@ -26,6 +28,7 @@ public class DOMTest extends TestCase object.addListener(this); TestObject to = (TestObject)object; + _test = to; // test transactions to.startTransaction(); @@ -33,6 +36,14 @@ public class DOMTest extends TestCase to.setBar("hoopie"); to.commitTransaction(); + // set some elements + to.setIntsAt(15, 3); + to.setIntsAt(5, 2); + to.setIntsAt(1, 0); + to.setStringsAt("Hello", 0); + to.setStringsAt("Goodbye", 1); + to.setStringsAt(null, 1); + // now set some values straight up to.setFoo(25); to.setBar("howdy"); @@ -56,6 +67,13 @@ public class DOMTest extends TestCase } } + public void elementUpdated (ElementUpdatedEvent event) + { +// Log.info("Element updated " + event); +// Log.info(StringUtil.toString(_test.ints)); +// Log.info(StringUtil.toString(_test.strings)); + } + public void runTest () { // request that a new TestObject be created @@ -80,6 +98,7 @@ public class DOMTest extends TestCase } protected int _fcount = 0; + protected TestObject _test; // the fields that will change in attribute changed events protected Object[] fields = { diff --git a/tests/src/java/com/threerings/presents/server/TestObject.dobj b/tests/src/java/com/threerings/presents/server/TestObject.dobj index 01030c02a..22e5a1f38 100644 --- a/tests/src/java/com/threerings/presents/server/TestObject.dobj +++ b/tests/src/java/com/threerings/presents/server/TestObject.dobj @@ -1,5 +1,5 @@ // -// $Id: TestObject.dobj,v 1.7 2002/02/08 23:17:38 mdb Exp $ +// $Id: TestObject.dobj,v 1.8 2002/03/19 01:10:03 mdb Exp $ package com.threerings.presents.server; @@ -12,5 +12,7 @@ public class TestObject extends DObject { public int foo; public String bar; + public int[] ints = new int[5]; + public String[] strings = new String[5]; public OidList list = new OidList(); } diff --git a/tests/src/java/com/threerings/presents/server/TestObject.java b/tests/src/java/com/threerings/presents/server/TestObject.java index d2749c507..fe0d22dc7 100644 --- a/tests/src/java/com/threerings/presents/server/TestObject.java +++ b/tests/src/java/com/threerings/presents/server/TestObject.java @@ -1,5 +1,5 @@ // -// $Id: TestObject.java,v 1.2 2002/02/20 23:35:42 mdb Exp $ +// $Id: TestObject.java,v 1.3 2002/03/19 01:10:03 mdb Exp $ package com.threerings.presents.server; @@ -16,11 +16,19 @@ public class TestObject extends DObject /** The field name of the bar field. */ public static final String BAR = "bar"; + /** The field name of the ints field. */ + public static final String INTS = "ints"; + + /** The field name of the strings field. */ + public static final String STRINGS = "strings"; + /** The field name of the list field. */ public static final String LIST = "list"; public int foo; public String bar; + public int[] ints = new int[5]; + public String[] strings = new String[5]; public OidList list = new OidList(); /** @@ -51,6 +59,62 @@ public class TestObject extends DObject requestAttributeChange(BAR, bar); } + /** + * Requests that the ints 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 setInts (int[] ints) + { + this.ints = ints; + requestAttributeChange(INTS, ints); + } + + /** + * Requests that the indexth element of + * ints 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 setIntsAt (int value, int index) + { + this.ints[index] = value; + requestElementUpdate(INTS, new Integer(value), index); + } + + /** + * Requests that the strings 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 setStrings (String[] strings) + { + this.strings = strings; + requestAttributeChange(STRINGS, strings); + } + + /** + * Requests that the indexth element of + * strings 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 setStringsAt (String value, int index) + { + this.strings[index] = value; + requestElementUpdate(STRINGS, value, index); + } + /** * Requests that the specified oid be added to the * list oid list. The list will not change until the