diff --git a/src/main/java/com/threerings/admin/client/FieldEditor.java b/src/main/java/com/threerings/admin/client/FieldEditor.java index 5a0aa3733..5ff872174 100644 --- a/src/main/java/com/threerings/admin/client/FieldEditor.java +++ b/src/main/java/com/threerings/admin/client/FieldEditor.java @@ -39,7 +39,6 @@ import com.samskivert.swing.HGroupLayout; import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.AttributeChangedEvent; import com.threerings.presents.dobj.DObject; -import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.util.PresentsContext; import static com.threerings.admin.Log.log; @@ -215,11 +214,7 @@ public abstract class FieldEditor extends JPanel public void set (Field field, Object value) { - try { - _obj.changeAttribute(field.getName(), value); - } catch (ObjectAccessException oae) { - log.warning("Failed to update field " + field.getName() + ": " + oae); - } + _obj.changeAttribute(field.getName(), value); } public Object get (Field field) diff --git a/src/main/java/com/threerings/admin/server/ConfigRegistry.java b/src/main/java/com/threerings/admin/server/ConfigRegistry.java index 723e20373..a56803d66 100644 --- a/src/main/java/com/threerings/admin/server/ConfigRegistry.java +++ b/src/main/java/com/threerings/admin/server/ConfigRegistry.java @@ -50,7 +50,6 @@ import com.threerings.presents.dobj.ElementUpdatedEvent; import com.threerings.presents.dobj.EntryAddedEvent; import com.threerings.presents.dobj.EntryRemovedEvent; import com.threerings.presents.dobj.EntryUpdatedEvent; -import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.dobj.SetListener; import static com.threerings.admin.Log.log; @@ -207,13 +206,7 @@ public abstract class ConfigRegistry // from ElementUpdateListener public void elementUpdated (ElementUpdatedEvent event) { - Object value; - try { - value = object.getAttribute(event.getName()); - } catch (ObjectAccessException oae) { - log.warning("Exception getting field", "name", event.getName(), "exception", oae); - return; - } + Object value = object.getAttribute(event.getName()); updateValue(event.getName(), value); } @@ -349,13 +342,7 @@ public abstract class ConfigRegistry protected void serializeAttribute (String attributeName) { String key = nameToKey(attributeName); - Object value; - try { - value = object.getAttribute(attributeName); - } catch (ObjectAccessException oae) { - log.warning("Exception getting field", "name", attributeName, "error", oae); - return; - } + Object value = object.getAttribute(attributeName); if (value == null || Streamer.isStreamable(value.getClass())) { serialize(attributeName, key, value); diff --git a/src/main/java/com/threerings/admin/web/server/ConfigServlet.java b/src/main/java/com/threerings/admin/web/server/ConfigServlet.java index bfb7a7ea9..e0a4ad766 100644 --- a/src/main/java/com/threerings/admin/web/server/ConfigServlet.java +++ b/src/main/java/com/threerings/admin/web/server/ConfigServlet.java @@ -37,7 +37,6 @@ import com.google.common.collect.Maps; import com.threerings.presents.data.InvocationCodes; import com.threerings.presents.dobj.DObject; -import com.threerings.presents.dobj.ObjectAccessException; import com.threerings.presents.dobj.RootDObjectManager; import com.threerings.web.gwt.ServiceException; @@ -103,12 +102,8 @@ public abstract class ConfigServlet extends RemoteServiceServlet int updateCount = 0; for (ConfigField update : updates) { - try { - object.changeAttribute(update.name, update.type.toValue(update.valStr)); - updateCount ++; - } catch (ObjectAccessException oae) { - log.warning("Failed to update field", "field", update.name, oae); - } + object.changeAttribute(update.name, update.type.toValue(update.valStr)); + updateCount ++; } object.commitTransaction(); diff --git a/src/main/java/com/threerings/presents/dobj/Accessor.java b/src/main/java/com/threerings/presents/dobj/Accessor.java new file mode 100644 index 000000000..97112d8f6 --- /dev/null +++ b/src/main/java/com/threerings/presents/dobj/Accessor.java @@ -0,0 +1,77 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved +// http://code.google.com/p/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 java.lang.reflect.Field; + +/** + * Used by {@link DObject} to provide dynamic access to its fields. This class is an implementation + * detail that can be safely ignored. It is only a public top-level class to ensure that bindings + * for other languages can make use of it without complications. + */ +public abstract class Accessor implements Comparable { + /** An accessor that assumes DObject fields are public Java fields. */ + public static class ByField extends Accessor { + public final Field field; + + public ByField (Field field) { + super(field.getName()); + this.field = field; + } + + public Object get (DObject obj) { + try { + return field.get(obj); + } catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + + public void set (DObject obj, Object value) { + try { + field.set(obj, value); + } catch (IllegalAccessException iae) { + throw new RuntimeException(iae); + } + } + } + + /** The name of this attribute. */ + public final String name; + + /** Gets the current value of this attribute. */ + public abstract Object get (DObject obj); + + /** Updates the current value of this attribute. */ + public abstract void set (DObject obj, Object value); + + // from interface Comparable + public int compareTo (Accessor other) + { + return name.compareTo(other.name); + } + + protected Accessor (String name) + { + this.name = name; + } +} diff --git a/src/main/java/com/threerings/presents/dobj/DObject.java b/src/main/java/com/threerings/presents/dobj/DObject.java index c41f50d25..f0e95e0bb 100644 --- a/src/main/java/com/threerings/presents/dobj/DObject.java +++ b/src/main/java/com/threerings/presents/dobj/DObject.java @@ -23,6 +23,7 @@ package com.threerings.presents.dobj; import java.lang.ref.WeakReference; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Comparator; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Map; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.samskivert.util.ArrayUtil; @@ -124,11 +126,11 @@ public class DObject { public DObject () { - _fields = _ftable.get(getClass()); - if (_fields == null) { - _fields = getClass().getFields(); - Arrays.sort(_fields, FIELD_COMP); - _ftable.put(getClass(), _fields); + _accessors = _atable.get(getClass()); + if (_accessors == null) { + _accessors = createAccessors(); + Arrays.sort(_accessors); + _atable.put(getClass(), _accessors); } } @@ -305,12 +307,8 @@ public class DObject */ public final DSet getSet (String setName) { - try { - @SuppressWarnings("unchecked") DSet casted = (DSet)getField(setName).get(this); - return casted; - } catch (Exception e) { - throw new IllegalArgumentException("No such set: " + setName); - } + @SuppressWarnings("unchecked") DSet casted = (DSet)getAccessor(setName).get(this); + return casted; } /** @@ -520,18 +518,10 @@ public class DObject * 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=" + StringUtil.shortClassName(value) + "]."; - throw new ObjectAccessException(errmsg, e); - } + Accessor acc = getAccessor(name); + requestAttributeChange(name, value, acc.get(this)); + acc.set(this, value); } /** @@ -540,16 +530,8 @@ public class DObject * attribute setter methods instead. */ public void setAttribute (String name, Object value) - throws ObjectAccessException { - try { - getField(name).set(this, value); - - } catch (Exception e) { - String errmsg = "setAttribute() failure [name=" + name + ", value=" + value + - ", vclass=" + StringUtil.shortClassName(value) + "]."; - throw new ObjectAccessException(errmsg, e); - } + getAccessor(name).set(this, value); } /** @@ -558,13 +540,8 @@ public class DObject * the generated attribute getter methods instead. */ public Object getAttribute (String name) - throws ObjectAccessException { - try { - return getField(name).get(this); - } catch (Exception e) { - throw new ObjectAccessException("getAttribute() failure [name=" + name + "].", e); - } + return getAccessor(name).get(this); } /** @@ -997,15 +974,16 @@ public class DObject } /** - * Returns the {@link Field} with the specified name or null if there is none such. + * Returns the {@link Accessor} for the field with the specified name throws an {@link + * IllegalArgumentException}. */ - protected final Field getField (String name) + protected final Accessor getAccessor (String name) { - int low = 0, high = _fields.length-1; + int low = 0, high = _accessors.length-1; while (low <= high) { int mid = (low + high) >> 1; - Field midVal = _fields[mid]; - int cmp = midVal.getName().compareTo(name); + Accessor midVal = _accessors[mid]; + int cmp = midVal.name.compareTo(name); if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { @@ -1014,7 +992,25 @@ public class DObject return midVal; // key found } } - return null; // key not found. + throw new IllegalArgumentException("No such field " + getClass().getName() + "." + name); + } + + /** + * Creates the accessors that will be used to read and write this object's attributes. The + * default implementation assumes the object's attributes are all public fields and uses + * reflection to get and set their values. + */ + protected Accessor[] createAccessors () + { + Field[] fields = getClass().getFields(); + // assume we have one static field for every non-static field + List accs = Lists.newArrayListWithExpectedSize(fields.length/2); + for (Field field : fields) { + if (!Modifier.isStatic(field.getModifiers())) { // skip static fields + accs.add(new Accessor.ByField(field)); + } + } + return accs.toArray(new Accessor[accs.size()]); } /** @@ -1038,8 +1034,8 @@ public class DObject /** Our object id. */ protected int _oid; - /** An array of our fields, sorted for efficient lookup. */ - protected transient Field[] _fields; + /** An array of our field accessors, sorted for efficient lookup. */ + protected transient Accessor[] _accessors; /** A reference to our object manager. */ protected transient DObjectManager _omgr; @@ -1074,15 +1070,8 @@ public class DObject /** Any local attributes configured on this object or null. */ protected transient Object[] _locattrs = NO_ATTRS; - /** Maintains a mapping of sorted field arrays for each distributed object class. */ - protected static Map, Field[]> _ftable = Maps.newHashMap(); - - /** Used to sort and search {@link #_fields}. */ - protected static final Comparator FIELD_COMP = new Comparator() { - public int compare (Field f1, Field f2) { - return f1.getName().compareTo(f2.getName()); - } - }; + /** Maintains a mapping of sorted accessor arrays for each distributed object class. */ + protected static Map, Accessor[]> _atable = Maps.newHashMap(); /** Simplifies code for objects that have no local attributes. */ protected static final Object[] NO_ATTRS = {};