From e206d26515f21c3dd70bd405e85261e3fe6cdf9e Mon Sep 17 00:00:00 2001 From: Mike Thomas Date: Thu, 4 Feb 2010 22:22:49 +0000 Subject: [PATCH] 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 --- .../threerings/admin/client/DSetEditor.java | 263 ++++++++++++------ .../threerings/admin/client/FieldEditor.java | 84 +++++- .../admin/client/TabbedDSetEditor.java | 14 + 3 files changed, 259 insertions(+), 102 deletions(-) diff --git a/src/java/com/threerings/admin/client/DSetEditor.java b/src/java/com/threerings/admin/client/DSetEditor.java index f82180f83..4e9b1fa7b 100644 --- a/src/java/com/threerings/admin/client/DSetEditor.java +++ b/src/java/com/threerings/admin/client/DSetEditor.java @@ -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 extends JPanel - implements AttributeChangeListener, SetListener, ActionListener + implements ActionListener { + public interface Accessor + { + 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 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(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 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 extends JPanel } } - // documentation inherited from interface SetListener - public void entryAdded (EntryAddedEvent event) - { - if (event.getName().equals(_setName)) { - addEntry(event.getEntry()); - } - } - - // documentation inherited from interface SetListener - public void entryRemoved (EntryRemovedEvent event) - { - if (event.getName().equals(_setName)) { - removeKey(event.getKey()); - } - } - - // documentation inherited from interface SetListener - public void entryUpdated (EntryUpdatedEvent 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> keys, Object[] data) { - _keys = new ComparableArrayList>(); - 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 getFilter () + { + return _entryFilter; + } - } else { - // Do some shuffling to get out a filtered array. - Iterator itr = Iterators.filter(_set.iterator(), _entryFilter); - ArrayList 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 extends JPanel return (Comparable)entry.getKey(); } - /** The object that contains the set we're displaying. */ - protected DObject _setter; + protected class DObjectAccessor + implements AttributeChangeListener, SetListener, Accessor + { + 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 event) + { + if (event.getName().equals(_setName)) { + addEntry(event.getEntry()); + } + } + + // documentation inherited from interface SetListener + public void entryRemoved (EntryRemovedEvent event) + { + if (event.getName().equals(_setName)) { + removeKey(event.getKey()); + } + } + + protected void refreshData () + { + ComparableArrayList> keys = + new ComparableArrayList>(); + E[] entries; + + if (_entryFilter == null) { + entries = createArray(); + + } else { + // Do some shuffling to get out a filtered array. + Iterator itr = Iterators.filter(iterator(), _entryFilter); + ArrayList 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 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 iterator () + { + return _set.iterator(); + } + + protected DObject _obj; + + protected DSet _set; + + protected String _setName; + } /** The name of the set in that object. */ protected String _setName; @@ -287,15 +371,20 @@ public class DSetEditor extends JPanel /** An optional predicate to decide whether actually care about displaying a given entry. */ protected Predicate _entryFilter; - /** The set itself. */ - protected DSet _set; - - /** An array we use to track our entries' positions by key. */ - protected ComparableArrayList> _keys; + /** Provides access to our data we're editing. */ + protected Accessor _accessor; /** The table used to edit. */ protected ObjectEditorTable _table; + /** An array we use to track our entries' positions by key. */ + protected ComparableArrayList> _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; } diff --git a/src/java/com/threerings/admin/client/FieldEditor.java b/src/java/com/threerings/admin/client/FieldEditor.java index 6a9769377..a8ecc668f 100644 --- a/src/java/com/threerings/admin/client/FieldEditor.java +++ b/src/java/com/threerings/admin/client/FieldEditor.java @@ -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; } diff --git a/src/java/com/threerings/admin/client/TabbedDSetEditor.java b/src/java/com/threerings/admin/client/TabbedDSetEditor.java index 99dd80810..2b0918b4d 100644 --- a/src/java/com/threerings/admin/client/TabbedDSetEditor.java +++ b/src/java/com/threerings/admin/client/TabbedDSetEditor.java @@ -56,6 +56,11 @@ import com.threerings.presents.dobj.SetListener; public class TabbedDSetEditor extends JPanel implements AttributeChangeListener, SetListener { + public interface AccessorFactory + { + public DSetEditor.Accessor createAccessor (DSetEditor editor); + } + /** * Used to divide various entires into different groups. */ @@ -170,6 +175,11 @@ public class TabbedDSetEditor extends JPanel add(_tabs); } + public void setAccessorFactory (AccessorFactory accessorFactory) + { + _accessorFactory = accessorFactory; + } + protected void computeTabs () { _grouper.computeGroups(_setter.getSet(_setName)); @@ -179,6 +189,9 @@ public class TabbedDSetEditor extends JPanel if (!_editors.containsKey(group)) { DSetEditor 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 extends JPanel protected final String[] _editableFields; protected final ObjectEditorTable.FieldInterpreter _interp; protected final EntryGrouper _grouper; + protected AccessorFactory _accessorFactory; protected JTabbedPane _tabs; protected HashMap> _editors = Maps.newHashMap();