From 890bad996e22dcb9c1d3e7b039736d9f0dc223f9 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 16 Aug 2001 03:45:43 +0000 Subject: [PATCH] Added ability to update elements in a set. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@266 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/presents/dobj/DObject.java | 13 +- .../com/threerings/presents/dobj/DSet.java | 29 +++- .../presents/dobj/EntryAddedEvent.java | 83 +---------- .../presents/dobj/EntryRemovedEvent.java | 4 +- .../presents/dobj/EntryUpdatedEvent.java | 136 ++++++++++++++++++ .../presents/dobj/io/EntryUtil.java | 85 +++++++++++ 6 files changed, 270 insertions(+), 80 deletions(-) create mode 100644 src/java/com/threerings/presents/dobj/EntryUpdatedEvent.java create mode 100644 src/java/com/threerings/presents/dobj/io/EntryUtil.java 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); +}