Abstract the manner by which DObject reads and writes its fields. This allows
us to create a Scala trait that handles reading and writing Scala "fields" which are not public fields but are rather private fields with public getters and setters. This also enables the creation of bridges for other JVM-hosted non-Java languages. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6391 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -39,7 +39,6 @@ import com.samskivert.swing.HGroupLayout;
|
|||||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
import com.threerings.presents.dobj.ObjectAccessException;
|
|
||||||
import com.threerings.presents.util.PresentsContext;
|
import com.threerings.presents.util.PresentsContext;
|
||||||
|
|
||||||
import static com.threerings.admin.Log.log;
|
import static com.threerings.admin.Log.log;
|
||||||
@@ -215,11 +214,7 @@ public abstract class FieldEditor extends JPanel
|
|||||||
|
|
||||||
public void set (Field field, Object value)
|
public void set (Field field, Object value)
|
||||||
{
|
{
|
||||||
try {
|
_obj.changeAttribute(field.getName(), value);
|
||||||
_obj.changeAttribute(field.getName(), value);
|
|
||||||
} catch (ObjectAccessException oae) {
|
|
||||||
log.warning("Failed to update field " + field.getName() + ": " + oae);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get (Field field)
|
public Object get (Field field)
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import com.threerings.presents.dobj.ElementUpdatedEvent;
|
|||||||
import com.threerings.presents.dobj.EntryAddedEvent;
|
import com.threerings.presents.dobj.EntryAddedEvent;
|
||||||
import com.threerings.presents.dobj.EntryRemovedEvent;
|
import com.threerings.presents.dobj.EntryRemovedEvent;
|
||||||
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||||
import com.threerings.presents.dobj.ObjectAccessException;
|
|
||||||
import com.threerings.presents.dobj.SetListener;
|
import com.threerings.presents.dobj.SetListener;
|
||||||
|
|
||||||
import static com.threerings.admin.Log.log;
|
import static com.threerings.admin.Log.log;
|
||||||
@@ -207,13 +206,7 @@ public abstract class ConfigRegistry
|
|||||||
// from ElementUpdateListener
|
// from ElementUpdateListener
|
||||||
public void elementUpdated (ElementUpdatedEvent event)
|
public void elementUpdated (ElementUpdatedEvent event)
|
||||||
{
|
{
|
||||||
Object value;
|
Object value = object.getAttribute(event.getName());
|
||||||
try {
|
|
||||||
value = object.getAttribute(event.getName());
|
|
||||||
} catch (ObjectAccessException oae) {
|
|
||||||
log.warning("Exception getting field", "name", event.getName(), "exception", oae);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateValue(event.getName(), value);
|
updateValue(event.getName(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,13 +342,7 @@ public abstract class ConfigRegistry
|
|||||||
protected void serializeAttribute (String attributeName)
|
protected void serializeAttribute (String attributeName)
|
||||||
{
|
{
|
||||||
String key = nameToKey(attributeName);
|
String key = nameToKey(attributeName);
|
||||||
Object value;
|
Object value = object.getAttribute(attributeName);
|
||||||
try {
|
|
||||||
value = object.getAttribute(attributeName);
|
|
||||||
} catch (ObjectAccessException oae) {
|
|
||||||
log.warning("Exception getting field", "name", attributeName, "error", oae);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == null || Streamer.isStreamable(value.getClass())) {
|
if (value == null || Streamer.isStreamable(value.getClass())) {
|
||||||
serialize(attributeName, key, value);
|
serialize(attributeName, key, value);
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import com.google.common.collect.Maps;
|
|||||||
|
|
||||||
import com.threerings.presents.data.InvocationCodes;
|
import com.threerings.presents.data.InvocationCodes;
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
import com.threerings.presents.dobj.ObjectAccessException;
|
|
||||||
import com.threerings.presents.dobj.RootDObjectManager;
|
import com.threerings.presents.dobj.RootDObjectManager;
|
||||||
|
|
||||||
import com.threerings.web.gwt.ServiceException;
|
import com.threerings.web.gwt.ServiceException;
|
||||||
@@ -103,12 +102,8 @@ public abstract class ConfigServlet extends RemoteServiceServlet
|
|||||||
|
|
||||||
int updateCount = 0;
|
int updateCount = 0;
|
||||||
for (ConfigField update : updates) {
|
for (ConfigField update : updates) {
|
||||||
try {
|
object.changeAttribute(update.name, update.type.toValue(update.valStr));
|
||||||
object.changeAttribute(update.name, update.type.toValue(update.valStr));
|
updateCount ++;
|
||||||
updateCount ++;
|
|
||||||
} catch (ObjectAccessException oae) {
|
|
||||||
log.warning("Failed to update field", "field", update.name, oae);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
object.commitTransaction();
|
object.commitTransaction();
|
||||||
|
|
||||||
|
|||||||
@@ -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<Accessor> {
|
||||||
|
/** 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<Accessor>
|
||||||
|
public int compareTo (Accessor other)
|
||||||
|
{
|
||||||
|
return name.compareTo(other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Accessor (String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ package com.threerings.presents.dobj;
|
|||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -30,6 +31,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import com.samskivert.util.ArrayUtil;
|
import com.samskivert.util.ArrayUtil;
|
||||||
@@ -124,11 +126,11 @@ public class DObject
|
|||||||
{
|
{
|
||||||
public DObject ()
|
public DObject ()
|
||||||
{
|
{
|
||||||
_fields = _ftable.get(getClass());
|
_accessors = _atable.get(getClass());
|
||||||
if (_fields == null) {
|
if (_accessors == null) {
|
||||||
_fields = getClass().getFields();
|
_accessors = createAccessors();
|
||||||
Arrays.sort(_fields, FIELD_COMP);
|
Arrays.sort(_accessors);
|
||||||
_ftable.put(getClass(), _fields);
|
_atable.put(getClass(), _accessors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,12 +307,8 @@ public class DObject
|
|||||||
*/
|
*/
|
||||||
public final <T extends DSet.Entry> DSet<T> getSet (String setName)
|
public final <T extends DSet.Entry> DSet<T> getSet (String setName)
|
||||||
{
|
{
|
||||||
try {
|
@SuppressWarnings("unchecked") DSet<T> casted = (DSet<T>)getAccessor(setName).get(this);
|
||||||
@SuppressWarnings("unchecked") DSet<T> casted = (DSet<T>)getField(setName).get(this);
|
return casted;
|
||||||
return casted;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalArgumentException("No such set: " + setName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -520,18 +518,10 @@ public class DObject
|
|||||||
* distributed fields in a generic manner.
|
* distributed fields in a generic manner.
|
||||||
*/
|
*/
|
||||||
public void changeAttribute (String name, Object value)
|
public void changeAttribute (String name, Object value)
|
||||||
throws ObjectAccessException
|
|
||||||
{
|
{
|
||||||
try {
|
Accessor acc = getAccessor(name);
|
||||||
Field f = getField(name);
|
requestAttributeChange(name, value, acc.get(this));
|
||||||
requestAttributeChange(name, value, f.get(this));
|
acc.set(this, value);
|
||||||
f.set(this, value);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
String errmsg = "changeAttribute() failure [name=" + name + ", value=" + value +
|
|
||||||
", vclass=" + StringUtil.shortClassName(value) + "].";
|
|
||||||
throw new ObjectAccessException(errmsg, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -540,16 +530,8 @@ public class DObject
|
|||||||
* attribute setter methods instead.
|
* attribute setter methods instead.
|
||||||
*/
|
*/
|
||||||
public void setAttribute (String name, Object value)
|
public void setAttribute (String name, Object value)
|
||||||
throws ObjectAccessException
|
|
||||||
{
|
{
|
||||||
try {
|
getAccessor(name).set(this, value);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,13 +540,8 @@ public class DObject
|
|||||||
* the generated attribute getter methods instead.
|
* the generated attribute getter methods instead.
|
||||||
*/
|
*/
|
||||||
public Object getAttribute (String name)
|
public Object getAttribute (String name)
|
||||||
throws ObjectAccessException
|
|
||||||
{
|
{
|
||||||
try {
|
return getAccessor(name).get(this);
|
||||||
return getField(name).get(this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ObjectAccessException("getAttribute() failure [name=" + name + "].", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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) {
|
while (low <= high) {
|
||||||
int mid = (low + high) >> 1;
|
int mid = (low + high) >> 1;
|
||||||
Field midVal = _fields[mid];
|
Accessor midVal = _accessors[mid];
|
||||||
int cmp = midVal.getName().compareTo(name);
|
int cmp = midVal.name.compareTo(name);
|
||||||
if (cmp < 0) {
|
if (cmp < 0) {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
} else if (cmp > 0) {
|
} else if (cmp > 0) {
|
||||||
@@ -1014,7 +992,25 @@ public class DObject
|
|||||||
return midVal; // key found
|
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<Accessor> 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. */
|
/** Our object id. */
|
||||||
protected int _oid;
|
protected int _oid;
|
||||||
|
|
||||||
/** An array of our fields, sorted for efficient lookup. */
|
/** An array of our field accessors, sorted for efficient lookup. */
|
||||||
protected transient Field[] _fields;
|
protected transient Accessor[] _accessors;
|
||||||
|
|
||||||
/** A reference to our object manager. */
|
/** A reference to our object manager. */
|
||||||
protected transient DObjectManager _omgr;
|
protected transient DObjectManager _omgr;
|
||||||
@@ -1074,15 +1070,8 @@ public class DObject
|
|||||||
/** Any local attributes configured on this object or null. */
|
/** Any local attributes configured on this object or null. */
|
||||||
protected transient Object[] _locattrs = NO_ATTRS;
|
protected transient Object[] _locattrs = NO_ATTRS;
|
||||||
|
|
||||||
/** Maintains a mapping of sorted field arrays for each distributed object class. */
|
/** Maintains a mapping of sorted accessor arrays for each distributed object class. */
|
||||||
protected static Map<Class<?>, Field[]> _ftable = Maps.newHashMap();
|
protected static Map<Class<?>, Accessor[]> _atable = Maps.newHashMap();
|
||||||
|
|
||||||
/** Used to sort and search {@link #_fields}. */
|
|
||||||
protected static final Comparator<Field> FIELD_COMP = new Comparator<Field>() {
|
|
||||||
public int compare (Field f1, Field f2) {
|
|
||||||
return f1.getName().compareTo(f2.getName());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Simplifies code for objects that have no local attributes. */
|
/** Simplifies code for objects that have no local attributes. */
|
||||||
protected static final Object[] NO_ATTRS = {};
|
protected static final Object[] NO_ATTRS = {};
|
||||||
|
|||||||
Reference in New Issue
Block a user