From ff5c1bf3e59c1760d9f8a02053b3cc4ea32ad4f7 Mon Sep 17 00:00:00 2001 From: Dave Hoover Date: Tue, 12 May 2009 21:13:54 +0000 Subject: [PATCH] Let us filter what entries a DSetEditor shows with a Predicate. And then build on that to make TabbedDSetEditor so that we can easily break up gigantic lists of entries into smaller & more manageable buckets. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5775 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/admin/client/DSetEditor.java | 113 ++++++++++++++---- .../admin/client/TabbedDSetEditor.java | 101 ++++++++++++++++ 2 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 src/java/com/threerings/admin/client/TabbedDSetEditor.java diff --git a/src/java/com/threerings/admin/client/DSetEditor.java b/src/java/com/threerings/admin/client/DSetEditor.java index d337cfdb9..3a8fe1e12 100644 --- a/src/java/com/threerings/admin/client/DSetEditor.java +++ b/src/java/com/threerings/admin/client/DSetEditor.java @@ -2,7 +2,7 @@ // $Id$ // // Narya library - tools for developing networked games -// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved +// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved // http://www.threerings.net/code/narya/ // // This library is free software; you can redistribute it and/or modify it @@ -25,11 +25,17 @@ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Iterator; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + import com.samskivert.util.ComparableArrayList; import com.samskivert.swing.ObjectEditorTable; @@ -55,7 +61,7 @@ public class DSetEditor extends JPanel * * @param setter The object that contains the set. * @param setName The name of the set in the object. - * @param entryClass the Class of the DSet.Entry elements contained in the set. + * @param entryClass The Class of the DSet.Entry elements contained in the set. */ public DSetEditor (DObject setter, String setName, Class entryClass) { @@ -67,11 +73,11 @@ public class DSetEditor extends JPanel * * @param setter The object that contains the set. * @param setName The name of the set in the object. - * @param entryClass the Class of the DSet.Entry elements contained in the set. + * @param entryClass The Class of the DSet.Entry elements contained in the set. * @param editableFields the names of the fields in the entryClass that should be editable. */ - public DSetEditor (DObject setter, String setName, Class entryClass, - String[] editableFields) + public DSetEditor ( + DObject setter, String setName, Class entryClass, String[] editableFields) { this(setter, setName, entryClass, editableFields, null); } @@ -82,17 +88,36 @@ public class DSetEditor extends JPanel * @param setter The object that contains the set. * @param setName The name of the set in the object. * @param entryClass the Class of the DSet.Entry elements contained in the set. - * @param editableFields the names of the fields in the entryClass that should be editable. + * @param editableFields The names of the fields in the entryClass that should be editable. * @param interp The FieldInterpreter to use. */ - public DSetEditor (DObject setter, String setName, Class entryClass, - String[] editableFields, ObjectEditorTable.FieldInterpreter interp) + public DSetEditor ( + DObject setter, String setName, Class entryClass, String[] editableFields, + ObjectEditorTable.FieldInterpreter interp) + { + this(setter, setName, entryClass, editableFields, interp, null); + } + + /** + * Construct a DSetEditor that only displays entries that match the given Predicate. + * + * @param setter The object that contains the set. + * @param setName The name of the set in the object. + * @param entryClass The Class of the DSet.Entry elements contained in the set. + * @param editableFields The names of the fields in the entryClass that should be editable. + * @param interp The FieldInterpreter to use. + * @param entryFilter The Predicate to use. + */ + public DSetEditor ( + DObject setter, String setName, Class entryClass, String[] editableFields, + ObjectEditorTable.FieldInterpreter interp, Predicate entryFilter) { super(new BorderLayout()); _setter = setter; _setName = setName; _set = _setter.getSet(setName); + _entryFilter = entryFilter; _table = new ObjectEditorTable(entryClass, editableFields, interp); @@ -142,13 +167,34 @@ public class DSetEditor extends JPanel super.removeNotify(); } + /** + * Handles the addition of an entry, assuming our filter allows it. + */ + protected void addEntry (E entry) + { + if (_entryFilter == null || _entryFilter.apply(entry)) { + int index = _keys.insertSorted(getKey(entry)); + _table.insertDatum(entry, index); + } + } + + /** + * Takes care of removing a key from + */ + protected void removeKey (Comparable key) + { + int index = _keys.indexOf(key); + if (index != -1) { + _keys.remove(index); + _table.removeDatum(index); + } + } + // documentation inherited from interface SetListener public void entryAdded (EntryAddedEvent event) { if (event.getName().equals(_setName)) { - E entry = event.getEntry(); - int index = _keys.insertSorted(getKey(entry)); - _table.insertDatum(entry, index); + addEntry(event.getEntry()); } } @@ -156,10 +202,7 @@ public class DSetEditor extends JPanel public void entryRemoved (EntryRemovedEvent event) { if (event.getName().equals(_setName)) { - Comparable key = event.getKey(); - int index = _keys.indexOf(key); - _keys.remove(index); - _table.removeDatum(index); + removeKey(event.getKey()); } } @@ -169,7 +212,18 @@ public class DSetEditor extends JPanel if (event.getName().equals(_setName)) { E entry = event.getEntry(); int index = _keys.indexOf(entry.getKey()); - _table.updateDatum(entry, index); + 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); + } } } @@ -193,10 +247,26 @@ public class DSetEditor extends JPanel protected void refreshData () { _keys = new ComparableArrayList>(); - @SuppressWarnings("unchecked") E[] entries = (E[])new DSet.Entry[_set.size()]; - _set.toArray(entries); - for (int ii = 0; ii < entries.length; ii++) { - _keys.insertSorted(getKey(entries[ii])); + E[] entries; + + if (_entryFilter == null) { + @SuppressWarnings("unchecked") E[] tmp = (E[])new DSet.Entry[_set.size()]; + entries = tmp; + _set.toArray(entries); + + } 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 } @@ -213,6 +283,9 @@ public class DSetEditor extends JPanel /** The name of the set in that object. */ protected String _setName; + /** An optional predicate to decide whether actually care about displaying a given entry. */ + protected Predicate _entryFilter; + /** The set itself. */ protected DSet _set; diff --git a/src/java/com/threerings/admin/client/TabbedDSetEditor.java b/src/java/com/threerings/admin/client/TabbedDSetEditor.java new file mode 100644 index 000000000..c81a30788 --- /dev/null +++ b/src/java/com/threerings/admin/client/TabbedDSetEditor.java @@ -0,0 +1,101 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/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.admin.client; + +import java.lang.reflect.Field; +import java.util.HashSet; + +import javax.swing.JPanel; +import javax.swing.JTabbedPane; + +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; + +import com.samskivert.util.Logger; +import com.samskivert.util.StringUtil; + +import com.samskivert.swing.ObjectEditorTable; + +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.DSet; + +/** + * Allows simple editing of DSets within a distributed object and easily groups entries into tabs + * based on the content of some field. + */ +public class TabbedDSetEditor extends JPanel +{ + /** + * A set of tabs containing DSetEditors grouping entries by the String value stored in + * a given field of the Entry. + */ + public TabbedDSetEditor ( + DObject setter, String setName, Class entryClass, + String[] editableFields, ObjectEditorTable.FieldInterpreter interp, String groupField) + { + DSet set = setter.getSet(setName); + + Field field; + + try { + field = entryClass.getField(groupField); + } catch (NoSuchFieldException nsfe) { + throw new IllegalArgumentException(Logger.format( + "Group field not found in prototype class!", + "proto", entryClass, "groupField", groupField)); + } + + HashSet groups = Sets.newHashSet(); + try { + for (E entry : set) { + groups.add(StringUtil.toString(field.get(entry))); + } + } catch (IllegalAccessException iae) { + throw new IllegalArgumentException(Logger.format( + "Could not access group field on entry", "groupField", groupField, iae)); + } + + JTabbedPane tabs = new JTabbedPane(); + add(tabs); + + for (String group : groups) { + tabs.addTab(group, new DSetEditor(setter, setName, entryClass, editableFields, + interp, createPredicate(field, group))); + } + } + + protected Predicate createPredicate (final Field field, final String group) + { + return new Predicate() { + public boolean apply (E entry) { + String val = null; + try { + val = StringUtil.toString(field.get(entry)); + } catch (IllegalAccessException iae) { + // Badness, but let's just compare to the null string and be done + } + + return group.equals(val); + } + }; + } +}