LISTSERV mailing list manager LISTSERV 16.0

Help for MASON-INTEREST-L Archives


MASON-INTEREST-L Archives

MASON-INTEREST-L Archives


MASON-INTEREST-L@LISTSERV.GMU.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Monospaced Font

LISTSERV Archives

LISTSERV Archives

MASON-INTEREST-L Home

MASON-INTEREST-L Home

MASON-INTEREST-L  June 2005

MASON-INTEREST-L June 2005

Subject:

Re: Integer Intervals and Domains

From:

Sean Luke <[log in to unmask]>

Reply-To:

MASON Multiagent Simulation Toolkit <[log in to unmask]>

Date:

Sat, 18 Jun 2005 16:29:01 -0400

Content-Type:

multipart/mixed

Parts/Attachments:

Parts/Attachments

text/plain (17 lines) , Properties.java (246 lines) , Unknown Name (5 lines) , PropertyField.java (415 lines) , Unknown Name (4 lines)

Fixed.

There are two bugs actually:

1. If you enter "1.0" (or any x.0) in a field whose value is supposed
to be an int/long/etc., you get an error. Instead it's best that such
integer values be converted to ints (i.e., 1.0 -> 1).

2. If you turn the slider for integer values, it sends non-integral
double values (1.23 etc.).

I've updated our internal code. Here are the two files I modified to
fix things. Let me know if these work for you.

sim/util/Properties.java


package sim.util;
import java.util.*;

/**
   The abstract superclass of Property inspectors. Such a beast inspects an object and returns a list of variables for which there are get and possibly set methods.

   <P>There are two such inspectors: SimpleProperties (inspects objects for their slots) and CollectionProperties (inspects Collections, Maps, and arrays for their contents). The easiest way to get an appropriate Properties subclass instance is to simply call the static method <b>Properties.getProperties(<i>object to inspect</i>, .... )</b>. See the SimpleProperties and CollectionProperties classes for specifics about how they define what a Property is. SimpleProperties in particular will beinteresting..

   <p>Property inspectors enumerate the Properties in their provided object. SimpleProperties will enumerate each of the slots; and CollectionProperties will enumerate the elements in the corresponding array, Map, Collection, etc. You get the number of Properties with numProperties(). Properties have the following features:

   <ul>
   <li><b>Property Name</b> (Readable). A string, usually the name of the instance variable in the object, a stringified number ("1", "203", etc.) for Collections or Arrays, or a stringified version of the key in a Map.
   <li><b>Property Value</b> (Readable and possibly Writable). An object representing the value of the property. Numbers etc. are wrapped in their corresponding wrapper classes.
   <li><b>Writability</b> (Readable).
   <li><b>Type</b> (Readable). The type of the object as a Class. ints return Integer.TYPE, etc.
   <li><b>Composite Nature</b> (Readable). Whether or not the Property is composite or atomic. Atomic types are int, float, etc., plus String.
   <li><b>Domain</b> (Readable). Whether or not the object has defined a Domain (a set or range of legal values) for the Property. Domains allow a GUI to set up sliders, pull-down menus and combo-boxes, etc.
   </ul>
*/

public abstract class Properties implements java.io.Serializable
    {
    /** Returns a Properties object for the given object.
        If expandCollections is true, then if object is a Map, Indexed, or Collection,
        then it will be treated using CollectionProperties. Otherwise it will be
        treated using SimpleProperties. Arrays are always treated using CollectionProperties.
        If includeSuperclasses is true, then any SimpleProperties will include superclasses.
        If includeGetClass is true, then the Class property will be included.
        No finite domains will be produced (that is, getDomain(index) will return null for
        all properties).
    */
    public static Properties getProperties(Object object, boolean expandCollections, boolean includeSuperclasses, boolean includeGetClass)
        {
        return getProperties(object,expandCollections,includeSuperclasses,includeGetClass,true);
        }

    /** Returns a Properties object for the given object.
        If expandCollections is true, then if object is a Map, Indexed, or Collection,
        then it will be treated using CollectionProperties. Otherwise it will be
        treated using SimpleProperties. Arrays are always treated using CollectionProperties.
        If includeSuperclasses is true, then any SimpleProperties will include superclasses.
        If includeGetClass is true, then the Class property will be included.
        If includeDomains is true (which requires a CollectionProperties), then domains
        will be produced for properties according to the rules in the comments in getDomain(index)
        below. Otherwise all objects will return a null (infinite) domain.
    */
    public static Properties getProperties(Object object, boolean expandCollections, boolean includeSuperclasses, boolean includeGetClass, boolean includeDomains)
        {
        if (object == null) return new SimpleProperties(object, includeSuperclasses, includeGetClass);
        Class c = object.getClass();
        if (c.isArray()) return new CollectionProperties(object);
        else if (expandCollections && (Collection.class.isAssignableFrom(c) ||
                                       Indexed.class.isAssignableFrom(c) ||
                                       Map.class.isAssignableFrom(c)))
            return new CollectionProperties(object);
        else return new SimpleProperties(object, includeSuperclasses, includeGetClass, includeDomains);
        }


    /** Returns true if the number or order of properties could change at any time */
    public abstract boolean isVolatile();

    /** Returns the number of properties discovered in the object. */
    public abstract int numProperties();

    /** Returns the value of the property at the given index. */
    public abstract Object getValue(int index);

    /** Returns the domain of the property at the given index.
        Domains are defined by methods of the form <tt>public Object dom<i>Property</i>()</tt>
        and should generally take one of three forms:
        <dl>
        <dt><tt>null</tt>
        <dd>no domain (domain is infinite).
        <dt>An array of elements
        <dd>the domain consists solely of those elements.
        <dt>A <tt>sim.util.Interval</tt>
        <dd>the domain is an inclusive (closed) numerical range defined by the Interval.
        If the Interval returns Longs, then the domain is considered to be integral; else
        it is considered to be real-valued.
        </dl>
    */
    public Object getDomain(int index) { return null; }

    /** Returns true if the property at the given index is both readable and writable (as opposed to read-only). */
    public abstract boolean isReadWrite(int index);

    /** Returns true if the property at the given index is a "Composite" object, meaning it's not a primitive type (double, int, etc.) nor a String. */
    public boolean isComposite(int index)
        {
        if (index < 0 || index > numProperties()) return false;
        Class type = getTypeConversion(getType(index));
        return !(type.isPrimitive() || type == String.class);
        }

    /** Returns the name of the property at the given index. */
    public abstract String getName(int index);

    /** Returns the Class (or for primitive objects, the primitive TYPE) of the property at the given index. */
    public abstract Class getType(int index);

    abstract Object _setValue(int index, Object value);

    /** Sets the current value of the property. Simple values (byte, int, etc.)
        must be boxed (into Byte, Integer, etc.). Then returns the current (hopefully changed) value of the property.
        Returns null if an error occurs or if the index is out of the range [0 ... numProperties() - 1 ]*/
    public Object setValue(int index, Object value)
        {
        // so we can also have a setValue (int, String) which calls US
        return _setValue(index, value);
        }

    /** Sets the current value of the property to the value parsed from the given string.
        Then returns the current (hopefully changed) value of the property.
        Returns null if an error occurs or if the index is out of the range [0 ... numProperties() - 1 ]*/
    public Object setValue(int index, String value)
        {
        try
            {
            Class type = getType(index);
            if ( type == Boolean.TYPE ) return _setValue(index,Boolean.valueOf(value));
            else if ( type == Byte.TYPE )
                {
                try { return _setValue(index,Byte.valueOf(value)); }
                catch (NumberFormatException e) // try again for x.0 stuff
                    {
                    double d = Double.parseDouble(value);
                    byte b = (byte) d;
                    if (b==d) return _setValue(index,new Byte(b));
                    else throw e;
                    }
                }
            else if ( type == Short.TYPE )
                {
                try { return _setValue(index,Short.valueOf(value)); }
                catch (NumberFormatException e) // try again for x.0 stuff
                    {
                    double d = Double.parseDouble(value);
                    short b = (short) d;
                    if (b==d) return _setValue(index,new Short(b));
                    else throw e;
                    }
                }
            else if ( type == Integer.TYPE )
                {
                try { return _setValue(index,Integer.valueOf(value)); }
                catch (NumberFormatException e) // try again for x.0 stuff
                    {
                    double d = Double.parseDouble(value);
                    int b = (int) d;
                    if (b==d) return _setValue(index,new Integer(b));
                    else throw e;
                    }
                }
            else if ( type == Long.TYPE )
                {
                try { return _setValue(index,Long.valueOf(value)); }
                catch (NumberFormatException e) // try again for x.0 stuff
                    {
                    double d = Double.parseDouble(value);
                    long b = (long) d;
                    if (b==d) return _setValue(index,new Long(b));
                    else throw e;
                    }
                }
            else if ( type == Float.TYPE ) return _setValue(index,Float.valueOf(value));
            else if ( type == Double.TYPE ) return _setValue(index,Double.valueOf(value));
            else if ( type == Character.TYPE ) return _setValue(index,new Character(value.charAt(0)));
            else if ( type == String.class ) return _setValue(index,value);
            else return null;
            }
        catch (Exception e)
            {
            e.printStackTrace();
            return null;
            }
        }

    /*
    f = new JFrame();
    h = new sim.app.heatbugs.HeatBugsWithUI();
    c = new sim.display.Console(h);
    i = new sim.portrayal.SimpleInspector(new Foo(),h,"Yo, Mama!");
    f.getContentPane().setLayout(new BorderLayout());
    f.getContentPane().add(i,BorderLayout.CENTER);
    f.pack();
    f.show();
    */

    // converts boxed type classes into simple types
    protected Class getTypeConversion(Class type)
        {
        if (type==Boolean.class || type==Boolean.TYPE)
            return Boolean.TYPE;
        else if (type==Byte.class || type==Byte.TYPE)
            return Byte.TYPE;
        else if (type==Short.class || type==Short.TYPE)
            return Short.TYPE;
        else if (type==Integer.class || type==Integer.TYPE)
            return Integer.TYPE;
        else if (type==Long.class || type==Long.TYPE)
            return Long.TYPE;
        else if (type==Float.class || type==Float.TYPE)
            return Float.TYPE;
        else if (type==Double.class || type==Double.TYPE)
            return Double.TYPE;
        else if (type==Character.class || type==Character.TYPE)
            return Character.TYPE;
        else return type;
        }

    /** Call this to get a prettier print-name for an object -- converting arrays to a nicer format, for example. */
    public String betterToString(Object obj)
        {
        if (obj == null) return "null";
        Class c = obj.getClass();
        if (c.isArray()) return typeToName(c) + "@" + Integer.toHexString(obj.hashCode());
        else return "" + obj;
        }

    protected String typeToName( Class type )
        {
        if ( type == null ) return null;

        if ( type.isPrimitive() )
            {
            return type.toString();
            }
        else if ( type == String.class )
            {
            return "String";
            }
        else if ( type.isArray() )
            {
            Class componentType = type.getComponentType();

            Class convertedComponentType = getTypeConversion(componentType);
            return typeToName(convertedComponentType) + "[]";
            }
        else
            return null;
        }

    }




sim/util/gui/PropertyField.java


package sim.util.gui;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.awt.*;
import java.util.*;
import sim.util.*;

/**
   A simple class designed to allow the user to modify a property in the form of a string, number, boolean value, or option. PropertyField lets you control the values which the user sets by subclassing the class and overriding the newValue(val) method filters all newly user-set values and "corrects" them. Programmatically set values (by calling setValue(...)) are not filtered through newValue by default. If you need to filter, you should do setValue(newValue(val));

   <p>You can optionally specify how the string will be presented to the user: as a text field, as a text field with
   a slider (requiring certain numerical constraints on the text field), as a list of options (also requiring certain numerical constraints), as a check box (requiring the string to hold boolean values ("true" or "false"), or as a
   read-only field with a button to press (which in turn calls the viewProperty() method, which you may override).

   <p>The specifics about how to present the user with these options is described in the constructor documentation and in the documentation for setValues(...).

   <p>PropertyFields can also be set to be either read-only or read/write by the user. When the user edits a
   read/write PropertyField, the text field changes color. If the user then presses RETURN, the result is submitted
   to newValue(...). If the user presses ESCAPE, the result is cancelled and reset.
*/

public class PropertyField extends JComponent
    {
    public JComboBox list = new JComboBox();
    public JTextField valField = new JTextField();
    public JCheckBox checkField = new JCheckBox();
    public JButton viewButton = new JButton("View"); // optionally displayed instead of valField (array or Object)
    public JLabel viewLabel = new JLabel();
    public JLabel optionalLabel = new JLabel();
    static final int SLIDER_MAX = 800000;
    static final int SLIDER_WIDTH = 80;
    public JSlider slider = new JSlider(0,SLIDER_MAX)
        {
        public Dimension getMaximumSize() { return new Dimension(SLIDER_WIDTH, super.getMaximumSize().height); }
        public Dimension getPreferredSize() { return getMaximumSize(); }
        };

    public Border valFieldBorder;
    public Border emptyBorder;
    public String currentValue;
    public boolean isReadWrite;
    public Object domain;

    public int displayState;
    public static final int SHOW_CHECKBOX = 0;
    public static final int SHOW_TEXTFIELD = 1;
    public static final int SHOW_VIEWBUTTON = 2;
    public static final int SHOW_SLIDER = 3;
    public static final int SHOW_LIST = 4;

    public Color defaultColor;
    public Color editedColor = new Color(225,225,255);
    public void setEditedColor(Color c) { editedColor = c; }
    public Color getEditedColor() { return editedColor; }

    public void submit()
        {
        if (edited) { setValue(newValue( valField.getText() )); }
        }

    public void update()
        {
        setValue(getValue());
        }

    boolean edited = false;
    void setEdited(boolean edited)
        {
        this.edited = edited;
        if (edited)
            {
            valField.setBackground(editedColor);
            }
        else
            {
            valField.setBackground(isReadWrite ? defaultColor : checkField.getBackground());
            }
        }

    public KeyListener listener = new KeyListener()
        {
        public void keyReleased(KeyEvent keyEvent) { }
        public void keyTyped(KeyEvent keyEvent) { }
        public void keyPressed(KeyEvent keyEvent)
            {
            if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
                {
                submit();
                }
            else if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE) // reset
                {
                update();
                }
            else
                {
                setEdited(true);
                }
            }
        };


      public ChangeListener checkListener = new ChangeListener()
        {
        public void stateChanged (ChangeEvent e)
            {
            setValue(newValue( new Boolean(checkField.isSelected()).toString() ));
            }
      };

    public ActionListener viewButtonListener = new ActionListener()
        {
        public void actionPerformed ( ActionEvent e )
            {
            viewProperty();
            }
        };

    public FocusAdapter focusAdapter = new FocusAdapter()
        {
        public void focusLost ( FocusEvent e )
            {
            submit();
            }
        };


    boolean sliding = false;

    public ChangeListener sliderListener = new ChangeListener()
        {
        public void stateChanged (ChangeEvent e)
            {
            if (domain != null && domain instanceof Interval)
                {
                double d = 0;
                Interval domain = (Interval)(PropertyField.this.domain);
                int i = slider.getValue();
                if (domain.isDouble())
                    {
                    double min = domain.getMin().doubleValue();
                    double max = domain.getMax().doubleValue();
                    d = (i / (double)SLIDER_MAX) * (max - min) + min;
                    }
                else // long
                    {
                    long min = domain.getMin().longValue();
                    long max = domain.getMax().longValue();
                    d = (double)((long)((i / (double)SLIDER_MAX) * (max - min) + min)); // floor to an integer value
                    }
                sliding = true;
                setValue(newValue("" + d));
                sliding = false;
                }
            }
        };

    public ActionListener listListener = new ActionListener()
        {
        public void actionPerformed ( ActionEvent e )
            {
            if (!settingList)
                setValue(newValue(""+list.getSelectedIndex()));
            }
        };

    boolean settingList = false;

    /** Sets the value, not filtering it through newValue(val) first. */
    public void setValue(String val)
        {
        switch(displayState)
            {
            case SHOW_SLIDER:
                setEdited(false);
                if (!sliding) { slide(val); }
                valField.setText(val);
                break;
            case SHOW_TEXTFIELD:
                setEdited(false);
                valField.setText(val);
                break;
            case SHOW_CHECKBOX:
                if(val!=null && val.equals("true"))
                    checkField.setSelected(true);
                else
                    checkField.setSelected(false);
                break;
            case SHOW_VIEWBUTTON:
                viewLabel.setText(val);
                break;
            case SHOW_LIST:
                settingList = true;
                try { list.setSelectedIndex(Integer.parseInt(val)); }
                catch (Exception e) { settingList = false; throw new RuntimeException(""+e); }
                settingList = false;
                break;
            default:
                break;
            }
        currentValue = val;
        }

    void slide(String val)
        {
        try
            {
            if (domain instanceof Interval)
                {
                Interval domain = (Interval)(this.domain);
                double d = Double.parseDouble(val);
                double min = domain.getMin().doubleValue();
                double max = domain.getMax().doubleValue();
                int i = (int)((d - min) / (max - min) * SLIDER_MAX);
                slider.setValue(i);
                }
            }
        catch (Exception e) { }
        }

    /** Returns the most recently set value. */
    public String getValue()
        {
        return currentValue;
        }

    /** Constructs a PropertyField as just a writeable, empty text field. */
    public PropertyField()
        {
        this(null,"",true);
        }

    /** Constructs a PropertyField as a writeable text field with the provided initial value. */
    public PropertyField(String initialValue)
        {
        this(null,initialValue,true);
        }

    /** Constructs a PropertyField as a text field with the provided initial value, either writeable or not. */
    public PropertyField(String initialValue, boolean isReadWrite)
        {
        this(null,initialValue,isReadWrite);
        }

    /** Constructs a labelled PropertyField as a writeable text field with the provided initial value. */
    public PropertyField(String label, String initialValue)
        {
        this(label,initialValue,true);
        }

    /** Constructs a labelled PropertyField as a text field with the provided initial value, either writeable or not. */
    public PropertyField(String label, String initialValue, boolean isReadWrite)
        {
        this(label,initialValue,isReadWrite, null, SHOW_TEXTFIELD);
        }

    /** Constructs a PropertyField with an optional label, an initial value, a "writeable" flag, an optional domain
        (for the slider and list options), and a display form (checkboxes, view buttons, text fields, sliders, or lists).
        <ul>
        <li>If show is SHOW_CHECKBOX, a checkbox will be shown (expecting "true" and "false" string values); pass in null for domain.
        <li>If show is SHOW_VIEWBUTTON, a view button will be shown (expecting a true object); pass in null for domain.
        <li>If show is SHOW_TEXTFIELD, a textfield will be shown; pass in null for domain.
        <li>If show is SHOW_SLIDER, both a textfield and a slider will be shown; the initialValue must be a number, and
        domain must be a sim.util.Interval.
        In this case, newValue(...) will be passed a String holding a number in the Interval range and must return
        a number. PropertyField will automatically make certain that the numbers are integral or real-valued; you
        do not need to check this so long as the Interval returns Longs or Doubles respectively. If isReadWrite is false,
        then the slider is not shown -- only the textfield.
        <li>If show is SHOW_LIST, a list will be shown; the initialValue must be an integer specifying the number in the list, and domain must be an array of Objects (strings, whatnot) or a java.util.List providing the objects in the list.
        In this case, newValue(...) will be passed a String holding a number; that number is the index in the list
        which the user has checked. newValue(...) must also return a String with the desired index for the list to be
        set to. */
    public PropertyField(String label, String initialValue, boolean isReadWrite, Object domain, int show)
        {
        // create object
        setLayout(new BorderLayout());
        add(optionalLabel,BorderLayout.WEST);

        valFieldBorder = valField.getBorder();
        Insets i = valFieldBorder.getBorderInsets(valField);
        emptyBorder = new EmptyBorder(i.top,i.left,i.bottom,i.right);

        defaultColor = valField.getBackground();
        valField.addKeyListener(listener);
        valField.addFocusListener(focusAdapter);
        viewButton.addActionListener(viewButtonListener);
        slider.addChangeListener(sliderListener);
        list.addActionListener(listListener);


        // set values
        setValues(label, initialValue, isReadWrite, domain, show);
        }

    /* Resets a PropertyField with an optional label, an initial value, a "writeable" flag, an optional domain
       (for the slider and list options), and a display form (checkboxes, view buttons, text fields, sliders, or lists).
       <ul>
       <li>If show is SHOW_CHECKBOX, a checkbox will be shown (expecting "true" and "false" string values); pass in null for domain.
       <li>If show is SHOW_VIEWBUTTON, a view button will be shown (expecting a true object); pass in null for domain.
       <li>If show is SHOW_TEXTFIELD, a textfield will be shown; pass in null for domain.
       <li>If show is SHOW_SLIDER, both a textfield and a slider will be shown; the initialValue must be a number, and
       domain must be a sim.util.Interval.
       In this case, newValue(...) will be passed a String holding a number in the Interval range and must return
       a number. PropertyField will automatically make certain that the numbers are integral or real-valued; you
       do not need to check this so long as the Interval returns Longs or Doubles respectively. If isReadWrite is false,
       then the slider is not shown -- only the textfield.
       <li>If show is SHOW_LIST, a list will be shown; the initialValue must be an integer specifying the number in the list, and domain must be an array of Objects (strings, whatnot) or a java.util.List providing the objects in the list.
       In this case, newValue(...) will be passed a String holding a number; that number is the index in the list
       which the user has checked. newValue(...) must also return a String with the desired index for the list to be
       set to.
    */
    public void setValues(String label, String initialValue, boolean isReadWrite, Object domain, int show)
        {
        this.domain = domain;
        removeAll();
        add(optionalLabel,BorderLayout.WEST);

        // some conversions
        if (show==SHOW_SLIDER && !isReadWrite) show = SHOW_TEXTFIELD;
        if (domain !=null && domain.getClass().isArray())
            {
            domain = Arrays.asList((Object[])domain);
            }

        displayState = show;
        switch(displayState)
            {
            case SHOW_SLIDER:
                JPanel p = new JPanel();
                p.setLayout(new BorderLayout());
                p.add(valField, BorderLayout.CENTER);
                if (isReadWrite && domain!=null && domain instanceof Interval)
                    p.add(slider, BorderLayout.WEST);
                add(p,BorderLayout.CENTER);
                break;
            case SHOW_TEXTFIELD:
                add(valField, BorderLayout.CENTER);
                break;
            case SHOW_CHECKBOX:
                add(checkField, BorderLayout.CENTER);
                break;
            case SHOW_VIEWBUTTON:
                add(viewButton, BorderLayout.EAST);
                add(viewLabel, BorderLayout.CENTER);
                break;
            case SHOW_LIST:
                if (domain != null && domain instanceof java.util.List)
                    {
                    settingList = true;
                    list.setEditable(false);
                    list.setModel(new DefaultComboBoxModel(new Vector((java.util.List)domain)));
                    add(list,BorderLayout.CENTER);
                    list.setEnabled(isReadWrite);
                    settingList = false;
                    }
                break;
            default:
                break;
            }
        revalidate();
        repaint();

        currentValue = initialValue;
        optionalLabel.setText(label);

        checkField.setEnabled(isReadWrite);
        valField.setEditable(isReadWrite);
        valField.setBorder(isReadWrite? valFieldBorder : emptyBorder);

        this.isReadWrite = isReadWrite;
        setValue(currentValue);
        }

    /** Override this to be informed when a new value has been set.
        The return value should be the value you want the display to show
        instead. */
    public String newValue(String newValue)
        {
        return newValue;
        }

    /** Override this to be informed when a property is to be viewed in its
        own inspector because the user pressed the "view" button. */
    public void viewProperty()
        {
        }

    public void setToolTipText(String text)
        {
        super.setToolTipText(text);
        valField.setToolTipText(text);
        checkField.setToolTipText(text);
        optionalLabel.setToolTipText(text);
        viewButton.setToolTipText(text);
        viewLabel.setToolTipText(text);
        slider.setToolTipText(text);
        list.setToolTipText(text);
        }

    public Dimension getMinimumSize()
        {
        Dimension s = super.getMinimumSize();
        s.height = valField.getMinimumSize().height;
        return s;
        }
    public Dimension getPreferredSize()
        {
        Dimension s = super.getPreferredSize();
        s.height = valField.getPreferredSize().height;
        return s;
        }
    }




Sean

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

January 2023
November 2022
April 2022
March 2022
January 2022
November 2021
May 2021
April 2021
March 2021
February 2021
August 2020
July 2020
June 2020
February 2020
August 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
December 2018
October 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
December 2017
November 2017
September 2017
June 2017
May 2017
March 2017
February 2017
January 2017
December 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
December 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
December 2010
November 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
February 2010
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
April 2005
March 2005
February 2005
January 2005
December 2004
November 2004
October 2004
September 2004
July 2004
May 2004
April 2004
March 2004

ATOM RSS1 RSS2



LISTSERV.GMU.EDU

CataList Email List Search Powered by the LISTSERV Email List Manager