I've broken out how the FieldEditors access their data into a separate Accessor plugin. This way, we can replace the default DObjectAccessor with (for example) an accessor that deals with multiple DObjects simultaneously.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6030 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Mike Thomas
2010-02-04 22:22:49 +00:00
parent c326538c07
commit e206d26515
3 changed files with 259 additions and 102 deletions
@@ -40,7 +40,6 @@ import com.samskivert.util.ComparableArrayList;
import com.samskivert.swing.ObjectEditorTable;
import com.samskivert.swing.event.CommandEvent;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.DObject;
@@ -54,8 +53,16 @@ import com.threerings.presents.dobj.SetListener;
* Allows simple editing of DSets within a distributed object.
*/
public class DSetEditor<E extends DSet.Entry> extends JPanel
implements AttributeChangeListener, SetListener<E>, ActionListener
implements ActionListener
{
public interface Accessor<F extends DSet.Entry>
{
void added ();
void removed ();
void updateEntry (String setName, DSet.Entry entry);
ObjectEditorTable.FieldInterpreter getInterp (ObjectEditorTable.FieldInterpreter interp);
}
/**
* Construct a DSet editor to merely display the specified set.
*
@@ -115,13 +122,24 @@ public class DSetEditor<E extends DSet.Entry> extends JPanel
{
super(new BorderLayout());
_setter = setter;
_setName = setName;
_set = _setter.getSet(setName);
_entryFilter = entryFilter;
_table = new ObjectEditorTable(entryClass, editableFields, interp, displayFields);
_entryClass = entryClass;
_editableFields = editableFields;
_interp = interp;
_displayFields = displayFields;
setAccessor(new DObjectAccessor<E>(setter, setName));
}
public void setAccessor (Accessor accessor)
{
removeAll();
_accessor = accessor;
_table = new ObjectEditorTable(_entryClass, _editableFields, _accessor.getInterp(_interp),
_displayFields);
add(new JScrollPane(_table), BorderLayout.CENTER);
}
@@ -153,17 +171,14 @@ public class DSetEditor<E extends DSet.Entry> extends JPanel
public void addNotify ()
{
super.addNotify();
_setter.addListener(this);
_accessor.added();
_table.addActionListener(this);
// populate the table
refreshData();
}
@Override
public void removeNotify ()
{
_setter.removeListener(this);
_accessor.removed();
_table.removeActionListener(this);
super.removeNotify();
}
@@ -191,85 +206,27 @@ public class DSetEditor<E extends DSet.Entry> extends JPanel
}
}
// documentation inherited from interface SetListener
public void entryAdded (EntryAddedEvent<E> event)
{
if (event.getName().equals(_setName)) {
addEntry(event.getEntry());
}
}
// documentation inherited from interface SetListener
public void entryRemoved (EntryRemovedEvent<E> event)
{
if (event.getName().equals(_setName)) {
removeKey(event.getKey());
}
}
// documentation inherited from interface SetListener
public void entryUpdated (EntryUpdatedEvent<E> event)
{
if (event.getName().equals(_setName)) {
E entry = event.getEntry();
int index = _keys.indexOf(entry.getKey());
if (index != -1) {
// We have it, so either update or remove
if (_entryFilter == null || _entryFilter.apply(entry)) {
_table.updateDatum(entry, index);
} else {
removeKey(entry.getKey());
}
} else {
// We DON'T have it, so try to add it in case we care about it
addEntry(entry);
}
}
}
// documentation inherited from interface SetListener
public void attributeChanged (AttributeChangedEvent event)
{
if (event.getName().equals(_setName)) {
// the whole set changed so we need to refetch it from the object
_set = _setter.getSet(_setName);
refreshData();
}
}
// documentation inherited from interface ActionListener
public void actionPerformed (ActionEvent event)
{
CommandEvent ce = (CommandEvent)event;
_setter.updateSet(_setName, (DSet.Entry)ce.getArgument());
_accessor.updateEntry(_setName, (DSet.Entry)ce.getArgument());
}
protected void refreshData ()
public void setData (ComparableArrayList<Comparable<Object>> keys, Object[] data)
{
_keys = new ComparableArrayList<Comparable<Object>>();
E[] entries;
_keys = keys;
_table.setData(data);
}
if (_entryFilter == null) {
@SuppressWarnings("unchecked") E[] tmp = (E[])new DSet.Entry[_set.size()];
entries = tmp;
_set.toArray(entries);
public Predicate<E> getFilter ()
{
return _entryFilter;
}
} else {
// Do some shuffling to get out a filtered array.
Iterator<E> itr = Iterators.filter(_set.iterator(), _entryFilter);
ArrayList<E> list = Lists.newArrayList();
Iterators.addAll(list, itr);
@SuppressWarnings("unchecked") E[] tmp = (E[])new DSet.Entry[list.size()];
entries = tmp;
list.toArray(entries);
}
for (E entry : entries) {
_keys.insertSorted(getKey(entry));
}
_table.setData(entries); // this works because DSet itself is sorted
public String getSetName ()
{
return _setName;
}
@SuppressWarnings("unchecked")
@@ -278,8 +235,135 @@ public class DSetEditor<E extends DSet.Entry> extends JPanel
return (Comparable<Object>)entry.getKey();
}
/** The object that contains the set we're displaying. */
protected DObject _setter;
protected class DObjectAccessor<F extends E>
implements AttributeChangeListener, SetListener<F>, Accessor<F>
{
public DObjectAccessor (DObject obj, String setName)
{
_obj = obj;
_setName = setName;
}
public ObjectEditorTable.FieldInterpreter getInterp (
ObjectEditorTable.FieldInterpreter interp)
{
return interp;
}
public void added ()
{
_obj.addListener(this);
refreshSet();
refreshData();
}
public void removed ()
{
_obj.removeListener(this);
}
public void refreshSet ()
{
_set = _obj.getSet(_setName);
}
public void updateEntry (String setName, DSet.Entry entry)
{
_obj.updateSet(setName, entry);
}
// documentation inherited from interface SetListener
public void entryAdded (EntryAddedEvent<F> event)
{
if (event.getName().equals(_setName)) {
addEntry(event.getEntry());
}
}
// documentation inherited from interface SetListener
public void entryRemoved (EntryRemovedEvent<F> event)
{
if (event.getName().equals(_setName)) {
removeKey(event.getKey());
}
}
protected void refreshData ()
{
ComparableArrayList<Comparable<Object>> keys =
new ComparableArrayList<Comparable<Object>>();
E[] entries;
if (_entryFilter == null) {
entries = createArray();
} else {
// Do some shuffling to get out a filtered array.
Iterator<F> itr = Iterators.filter(iterator(), _entryFilter);
ArrayList<F> list = Lists.newArrayList();
Iterators.addAll(list, itr);
@SuppressWarnings("unchecked") F[] tmp = (F[])new DSet.Entry[list.size()];
entries = tmp;
list.toArray(entries);
}
for (E entry : entries) {
keys.insertSorted(getKey(entry));
}
setData(keys, entries); // this works because DSet itself is sorted
}
// documentation inherited from interface SetListener
public void entryUpdated (EntryUpdatedEvent<F> event)
{
if (event.getName().equals(_setName)) {
E entry = event.getEntry();
int index = _keys.indexOf(entry.getKey());
if (index != -1) {
// We have it, so either update or remove
if (_entryFilter == null || _entryFilter.apply(entry)) {
_table.updateDatum(entry, index);
} else {
removeKey(entry.getKey());
}
} else {
// We DON'T have it, so try to add it in case we care about it
addEntry(entry);
}
}
}
// documentation inherited from interface SetListener
public void attributeChanged (AttributeChangedEvent event)
{
if (event.getName().equals(_setName)) {
// the whole set changed so we need to refetch it from the object
refreshSet();
refreshData();
}
}
public E[] createArray ()
{
@SuppressWarnings("unchecked") F[] tmp = (F[])new DSet.Entry[_set.size()];
F[] entries = tmp;
_set.toArray(entries);
return entries;
}
public Iterator<F> iterator ()
{
return _set.iterator();
}
protected DObject _obj;
protected DSet<F> _set;
protected String _setName;
}
/** The name of the set in that object. */
protected String _setName;
@@ -287,15 +371,20 @@ public class DSetEditor<E extends DSet.Entry> extends JPanel
/** An optional predicate to decide whether actually care about displaying a given entry. */
protected Predicate<E> _entryFilter;
/** The set itself. */
protected DSet<E> _set;
/** An array we use to track our entries' positions by key. */
protected ComparableArrayList<Comparable<Object>> _keys;
/** Provides access to our data we're editing. */
protected Accessor<E> _accessor;
/** The table used to edit. */
protected ObjectEditorTable _table;
/** An array we use to track our entries' positions by key. */
protected ComparableArrayList<Comparable<Object>> _keys;
protected Class<?> _entryClass;
protected String[] _editableFields;
protected ObjectEditorTable.FieldInterpreter _interp;
protected String[] _displayFields;
/** The minimum height for our editor UI. */
protected static final int MIN_HEIGHT = 200;
}
@@ -51,11 +51,20 @@ import static com.threerings.admin.Log.log;
public abstract class FieldEditor extends JPanel
implements AttributeChangeListener, ActionListener, FocusListener
{
/** The interface defining how the editor interacts with its data. */
public interface Accessor
{
void added ();
void removed ();
void set (Field field, Object value);
Object get (Field field);
}
public FieldEditor (PresentsContext ctx, Field field, DObject object)
{
_ctx = ctx;
_field = field;
_object = object;
setAccessor(new DObjectAccessor(object));
// create our interface elements
setLayout(new HGroupLayout(HGroupLayout.STRETCH));
@@ -67,13 +76,18 @@ public abstract class FieldEditor extends JPanel
updateBorder(false);
}
public void setAccessor (Accessor accessor)
{
_accessor = accessor;
}
@Override
public void addNotify ()
{
super.addNotify();
// listen to the object while we're visible
_object.addListener(FieldEditor.this);
_accessor.added();
displayValue(getValue());
}
@@ -83,11 +97,17 @@ public abstract class FieldEditor extends JPanel
super.removeNotify();
// stop listening when we're hidden
_object.removeListener(FieldEditor.this);
_accessor.removed();
}
// documentation inherited from interface
public void attributeChanged (AttributeChangedEvent event)
{
noteUpdatedExternally();
}
/** Update ourselves to reflect a change from outside the editor. */
public void noteUpdatedExternally ()
{
displayValue(getValue());
updateBorder(false);
@@ -105,11 +125,7 @@ public abstract class FieldEditor extends JPanel
// submit an attribute changed event with the new value
if (!ObjectUtil.equals(value, getValue())) {
try {
_object.changeAttribute(_field.getName(), value);
} catch (ObjectAccessException oae) {
log.warning("Failed to update field " + _field.getName() + ": " + oae);
}
_accessor.set(_field, value);
}
}
@@ -151,12 +167,7 @@ public abstract class FieldEditor extends JPanel
*/
protected Object getValue ()
{
try {
return _field.get(_object);
} catch (Exception e) {
log.warning("Failed to fetch field", "field", _field, "object", _object, "error", e);
return null;
}
return _accessor.get(_field);
}
/**
@@ -172,8 +183,51 @@ public abstract class FieldEditor extends JPanel
}
}
/**
* A simple accessor that knows how to interact with a DObject - this is normally what is used.
*/
protected class DObjectAccessor
implements Accessor
{
public DObjectAccessor (DObject obj)
{
_obj = obj;
}
public void added ()
{
_obj.addListener(FieldEditor.this);
}
public void removed ()
{
_obj.removeListener(FieldEditor.this);
}
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);
}
}
public Object get (Field field)
{
try {
return field.get(_obj);
} catch (Exception e) {
log.warning("Failed to fetch field", "field", field, "object", _obj, "error", e);
return null;
}
}
protected DObject _obj;
}
protected PresentsContext _ctx;
protected Field _field;
protected DObject _object;
protected Accessor _accessor;
protected JLabel _label;
}
@@ -56,6 +56,11 @@ import com.threerings.presents.dobj.SetListener;
public class TabbedDSetEditor<E extends DSet.Entry> extends JPanel
implements AttributeChangeListener, SetListener<E>
{
public interface AccessorFactory
{
public DSetEditor.Accessor createAccessor (DSetEditor editor);
}
/**
* Used to divide various entires into different groups.
*/
@@ -170,6 +175,11 @@ public class TabbedDSetEditor<E extends DSet.Entry> extends JPanel
add(_tabs);
}
public void setAccessorFactory (AccessorFactory accessorFactory)
{
_accessorFactory = accessorFactory;
}
protected void computeTabs ()
{
_grouper.computeGroups(_setter.<E>getSet(_setName));
@@ -179,6 +189,9 @@ public class TabbedDSetEditor<E extends DSet.Entry> extends JPanel
if (!_editors.containsKey(group)) {
DSetEditor<E> editor = createEditor(
_setter, _setName, _entryClass, _editableFields, _interp, _grouper, group);
if (_accessorFactory != null) {
editor.setAccessor(_accessorFactory.createAccessor(editor));
}
_tabs.add(group, editor);
_editors.put(group, editor);
}
@@ -257,6 +270,7 @@ public class TabbedDSetEditor<E extends DSet.Entry> extends JPanel
protected final String[] _editableFields;
protected final ObjectEditorTable.FieldInterpreter _interp;
protected final EntryGrouper<E> _grouper;
protected AccessorFactory _accessorFactory;
protected JTabbedPane _tabs;
protected HashMap<String, DSetEditor<E>> _editors = Maps.newHashMap();