diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java
index c160181ff..af8597913 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.24 2001/08/16 03:31:08 mdb Exp $
+// $Id: DObject.java,v 1.25 2001/08/16 03:45:43 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -434,6 +434,17 @@ public class DObject
_mgr.postEvent(event);
}
+ /**
+ * Calls by derived instances when a set updater method was called.
+ */
+ protected void requestElementUpdate (String name, DSet.Element elem)
+ {
+ // generate an element updated event
+ DEvent event = new ElementUpdatedEvent(_oid, name, elem);
+ // and dispatch it to our dobjmgr
+ _mgr.postEvent(event);
+ }
+
/** Our object id. */
protected int _oid;
diff --git a/src/java/com/threerings/presents/dobj/DSet.java b/src/java/com/threerings/presents/dobj/DSet.java
index 1965587b8..ef9c475b1 100644
--- a/src/java/com/threerings/presents/dobj/DSet.java
+++ b/src/java/com/threerings/presents/dobj/DSet.java
@@ -1,5 +1,5 @@
//
-// $Id: DSet.java,v 1.1 2001/08/16 03:31:09 mdb Exp $
+// $Id: DSet.java,v 1.2 2001/08/16 03:45:43 mdb Exp $
package com.threerings.cocktail.cher.dobj;
@@ -219,6 +219,33 @@ public class DSet
return false;
}
+ /**
+ * Updates the specified element by locating an element whose key
+ * matches the key of the supplied element and overwriting it. This
+ * should not be called directly, instead the associated
+ * update{Set}() method should be called on the
+ * distributed object that contains the set in question.
+ *
+ * @return true if the element was updated, false if it was not
+ * already in the set (in which case nothing is updated).
+ */
+ protected boolean update (Element elem)
+ {
+ Object key = elem.getKey();
+
+ // scan the array looking for a matching element
+ int elength = _elements.length;
+ for (int i = 0; i < elength; i++) {
+ Element el = _elements[i];
+ if (el != null && el.getKey().equals(key)) {
+ _elements[i] = elem;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
protected Class _elementType;
protected Element[] _elements;
}
diff --git a/src/java/com/threerings/presents/dobj/EntryAddedEvent.java b/src/java/com/threerings/presents/dobj/EntryAddedEvent.java
index 87eba5167..c5e00eb4e 100644
--- a/src/java/com/threerings/presents/dobj/EntryAddedEvent.java
+++ b/src/java/com/threerings/presents/dobj/EntryAddedEvent.java
@@ -1,11 +1,14 @@
//
-// $Id: EntryAddedEvent.java,v 1.2 2001/08/16 03:33:11 mdb Exp $
+// $Id: EntryAddedEvent.java,v 1.3 2001/08/16 03:45:43 mdb Exp $
package com.threerings.cocktail.cher.dobj;
-import java.io.*;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
import com.threerings.cocktail.cher.Log;
+import com.threerings.cocktail.cher.dobj.io.ElementUtil;
/**
* An element added event is dispatched when an element is added to a
@@ -74,7 +77,7 @@ public class ElementAddedEvent extends TypedEvent
// element (if need be)
if (_elem == null) {
try {
- _elem = unflatten(set, _bytes);
+ _elem = ElementUtil.unflatten(set, _bytes);
} catch (Exception e) {
Log.warning("Error unflattening element " + this + ".");
Log.logStackTrace(e);
@@ -96,7 +99,7 @@ public class ElementAddedEvent extends TypedEvent
{
super.writeTo(out);
out.writeUTF(_name);
- flatten(out, _elem);
+ ElementUtil.flatten(out, _elem);
}
public void readFrom (DataInputStream in)
@@ -124,76 +127,4 @@ public class ElementAddedEvent extends TypedEvent
protected String _name;
protected byte[] _bytes;
protected DSet.Element _elem;
-
- /**
- * Because we don't know the type of the element at the time that the
- * event is read from the network (we only know it once the event is
- * being applied to the object and we can ask the target
- * DSet instance what type it manages), we flatten the
- * element before writing it to the wire so that we can prepend it by
- * a byte count. The receiver will read in the raw bytes and decode
- * them only later when it has access to the DSet
- * instance that knows what element type to use. This method should
- * really only be called by the conmgr thread, but we synchronize just
- * in case someone decides to write an event out in some other
- * peculiar context. Uncontested syncs are pretty fast.
- */
- protected static synchronized void flatten (
- DataOutputStream out, DSet.Element elem)
- throws IOException
- {
- elem.writeTo(_dout);
- _dout.flush();
- out.writeInt(_bout.size());
- _bout.writeTo(out);
- }
-
- /**
- * Unflattens an element given the serialized element data. We know
- * this will always be called on the dobjmgr thread, so we need not
- * synchronize.
- */
- protected static DSet.Element unflatten (DSet set, byte[] data)
- throws IOException
- {
- _bin.setBytes(data);
- DSet.Element elem = set.newElement();
- elem.readFrom(_din);
- return elem;
- }
-
- /**
- * We extend byte array input stream to avoid having to create a new
- * input stream every time we unserialize an element. Our extensions
- * allow us to repurpose this input stream to read from a new byte
- * array each time we unserialize.
- */
- protected static class ReByteArrayInputStream extends ByteArrayInputStream
- {
- public ReByteArrayInputStream ()
- {
- super(new byte[0]);
- }
-
- public void setBytes (byte[] bytes)
- {
- buf = bytes;
- pos = 0;
- count = buf.length;
- mark = 0;
- }
- }
-
- /** Used when serializing elements. */
- protected static ByteArrayOutputStream _bout = new ByteArrayOutputStream();
-
- /** Used when serializing elements. */
- protected static DataOutputStream _dout = new DataOutputStream(_bout);
-
- /** Used when unserializing elements. */
- protected static ReByteArrayInputStream _bin =
- new ReByteArrayInputStream();
-
- /** Used when unserializing elements. */
- protected static DataInputStream _din = new DataInputStream(_bin);
}
diff --git a/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java b/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java
index 2ad556d0a..907af63f5 100644
--- a/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java
+++ b/src/java/com/threerings/presents/dobj/EntryRemovedEvent.java
@@ -1,11 +1,11 @@
//
-// $Id: EntryRemovedEvent.java,v 1.2 2001/08/16 03:33:11 mdb Exp $
+// $Id: EntryRemovedEvent.java,v 1.3 2001/08/16 03:45:43 mdb Exp $
package com.threerings.cocktail.cher.dobj;
-import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.io.IOException;
import com.threerings.cocktail.cher.dobj.io.ValueMarshaller;
diff --git a/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java b/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java
new file mode 100644
index 000000000..0dd11450c
--- /dev/null
+++ b/src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java
@@ -0,0 +1,136 @@
+//
+// $Id: EntryUpdatedEvent.java,v 1.1 2001/08/16 03:45:43 mdb Exp $
+
+package com.threerings.cocktail.cher.dobj;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import com.threerings.cocktail.cher.Log;
+import com.threerings.cocktail.cher.dobj.io.ElementUtil;
+
+/**
+ * An element updated event is dispatched when an element of a
+ * DSet is updated. It can also be constructed to request the
+ * update of an element 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 + 10;
+
+ /**
+ * Constructs a new element updated event on the specified target
+ * object for the specified set name and with the supplied updated
+ * element.
+ *
+ * @param targetOid the object id of the object to whose set we will
+ * add an element.
+ * @param name the name of the attribute in which to update the
+ * specified element.
+ * @param elem the element to update.
+ */
+ public ElementUpdatedEvent (int targetOid, String name, DSet.Element elem)
+ {
+ super(targetOid);
+ _name = name;
+ _elem = elem;
+ }
+
+ /**
+ * Constructs a blank instance of this event in preparation for
+ * unserialization from the network.
+ */
+ public ElementUpdatedEvent ()
+ {
+ }
+
+ /**
+ * Returns the name of the set attribute for which an element has been
+ * updated.
+ */
+ public String getName ()
+ {
+ return _name;
+ }
+
+ /**
+ * Returns the element that has been updated.
+ */
+ public DSet.Element getElement ()
+ {
+ return _elem;
+ }
+
+ /**
+ * Applies this event to the object.
+ */
+ public boolean applyToObject (DObject target)
+ throws ObjectAccessException
+ {
+ DSet set = (DSet)target.getAttribute(_name);
+
+ // now that we have access to our target set, we can unflatten our
+ // element (if need be)
+ if (_elem == null) {
+ try {
+ _elem = ElementUtil.unflatten(set, _bytes);
+ } catch (Exception e) {
+ Log.warning("Error unflattening element " + this + ".");
+ Log.logStackTrace(e);
+ return false;
+ }
+ }
+
+ // update the element
+ if (!set.update(_elem)) {
+ // complain if we didn't update anything
+ Log.warning("No matching element to update " + this + ".");
+ return false;
+ }
+
+ return true;
+ }
+
+ public short getType ()
+ {
+ return TYPE;
+ }
+
+ public void writeTo (DataOutputStream out)
+ throws IOException
+ {
+ super.writeTo(out);
+ out.writeUTF(_name);
+ ElementUtil.flatten(out, _elem);
+ }
+
+ public void readFrom (DataInputStream in)
+ throws IOException
+ {
+ super.readFrom(in);
+ _name = in.readUTF();
+
+ // we read in the raw element data now and decode it later when we
+ // have access to the object and the DSet instance that knows what
+ // type of element we need to decode
+ int bcount = in.readInt();
+ _bytes = new byte[bcount];
+ in.readFully(_bytes, 0, bcount);
+ }
+
+ protected void toString (StringBuffer buf)
+ {
+ buf.append("ELUPD:");
+ super.toString(buf);
+ buf.append(", name=").append(_name);
+ buf.append(", elem=").append(_elem);
+ }
+
+ protected String _name;
+ protected byte[] _bytes;
+ protected DSet.Element _elem;
+}
diff --git a/src/java/com/threerings/presents/dobj/io/EntryUtil.java b/src/java/com/threerings/presents/dobj/io/EntryUtil.java
new file mode 100644
index 000000000..8635e060a
--- /dev/null
+++ b/src/java/com/threerings/presents/dobj/io/EntryUtil.java
@@ -0,0 +1,85 @@
+//
+// $Id: EntryUtil.java,v 1.1 2001/08/16 03:45:43 mdb Exp $
+
+package com.threerings.cocktail.cher.dobj.io;
+
+import java.io.*;
+
+import com.threerings.cocktail.cher.dobj.DSet;
+
+/**
+ * Routines to simplify the process of moving set elements over the wire.
+ * Because we don't know the type of the element when the event is
+ * unserialized (we only know later when the event is applied to the
+ * object and the event has access to the target set object), then we need
+ * to do some jockeying.
+ */
+public class ElementUtil
+{
+ /**
+ * Flattens the supplied element into a byte array, counts the number
+ * of bytes in the array and writes the count followed by the bytes to
+ * the supplied data output stream. This method should really only be
+ * called by the conmgr thread, but we synchronize just in case
+ * someone decides to write an event out in some other peculiar
+ * context; uncontested syncs are pretty fast.
+ */
+ public static synchronized void flatten (
+ DataOutputStream out, DSet.Element elem)
+ throws IOException
+ {
+ elem.writeTo(_dout);
+ _dout.flush();
+ out.writeInt(_bout.size());
+ _bout.writeTo(out);
+ }
+
+ /**
+ * Unflattens an element given the serialized element data. We know
+ * this will always be called on the dobjmgr thread, so we need not
+ * synchronize.
+ */
+ public static DSet.Element unflatten (DSet set, byte[] data)
+ throws IOException
+ {
+ _bin.setBytes(data);
+ DSet.Element elem = set.newElement();
+ elem.readFrom(_din);
+ return elem;
+ }
+
+ /**
+ * We extend byte array input stream to avoid having to create a new
+ * input stream every time we unserialize an element. Our extensions
+ * allow us to repurpose this input stream to read from a new byte
+ * array each time we unserialize.
+ */
+ protected static class ReByteArrayInputStream extends ByteArrayInputStream
+ {
+ public ReByteArrayInputStream ()
+ {
+ super(new byte[0]);
+ }
+
+ public void setBytes (byte[] bytes)
+ {
+ buf = bytes;
+ pos = 0;
+ count = buf.length;
+ mark = 0;
+ }
+ }
+
+ /** Used when serializing elements. */
+ protected static ByteArrayOutputStream _bout = new ByteArrayOutputStream();
+
+ /** Used when serializing elements. */
+ protected static DataOutputStream _dout = new DataOutputStream(_bout);
+
+ /** Used when unserializing elements. */
+ protected static ReByteArrayInputStream _bin =
+ new ReByteArrayInputStream();
+
+ /** Used when unserializing elements. */
+ protected static DataInputStream _din = new DataInputStream(_bin);
+}