Fundamentals of Computer Science II (CSC-152 99F)


Project Proposals, Stage One

This is my summary of what the various Teams gave me on Wednesday, September 29, 1999.

The Basics

Our goal for this first part was to make sure that everyone could tell enough about our subproject that they could think about using it to support their part of the subproject. This meant that, at minimum, you should have given a list of classes and the methods each class provides. Unfortunately, not all of you did even that. You'll need to make sure that all of that is carefully specified for the next round.

In addition, each Team was expected to give a description of the purpose of their classes, along with some explanation of design ideas. I saw this even more rarely.

I've shared the comments with the whole class because I think we need a class dialog on how to improve the various components.

Initial Questions

Teams

Team Aardvark

This team is responsible for the graphical user interface. They have no classes that anyone else will use, so there is not much that they had to document for that. However, they did need to provide some pictures of the interface which I will share. I do wonder what it looks like when you're reading a message. I also wonder whether it is possible to have a separate window for each message you read (I would like such a thing).

Team Bison

Although Ben and Joshua were responsible for only the network interface, it looks like they've decided to support the whole ``back end'' of the system, including Login, Mailbox, Folder, and Maildrop. My preference would be to combine the first three into one class, but it's up to them.

Team Camel

This group is responsible for filtering and sorting lists of messages (so that you can look for messages from a particular person, prioritize your message list, or sort according to some field). They've proposed two utility classes (MessageInfoList and Folder) that seem to work behind the scenes. The classes that others wil use include Rule and Condition. Right now, it looks like they only support filtering, and it's not totally clear how.

Team Chipmunk

This team is responsible for the picture database. I don't seem to have received anything from this team (or I've lost it if I did).

Team Emu

This team is responsible for preferences and the address book. They did not precisely detail the classes that they would support nor the methods that those classes would support. For example, how do I specify the user for whom we are getting preferences? What they did detail was the particular preferences they would support. It might be better to permit the client to specify what particular preferences they need supported and for you to support all of them. The mail classes this Team seems to have identified are Options, Profile, AddressBook

Team Flamingo

This team is responsible for the composing interface (and, I thought, for the Maildrop). They did not turn in a picture. It's not quite clear how one creates a new ``Compose'' window, nor how one indicates that it's time for the message to be send.

The Team Formerly Known as Group

This team is responsible for the textual user interface.

Team SamR

I appear to be responsible for the various utility classes, particularly since Ben and Joshua have decided to deal with the mailbox. I've worked on MessageHeader, MailMessage, and a few others.

Classes

AddressBook

Purpose: Stores information on addresses for the user. Can this reside on the IMAP server?

Methods: Not specified.

AttachmentInfo

Purpose: Stores information on attachments so that they can be included in outgoing and incoming messages. Still needs a lot more development.

Code:


/**
 * Information on attachments.  Implementation waiting for
 * further information on the handling of attachments.  Note
 * that we might use different information for incoming and
 * outgoing attachments (incoming attachments have information
 * on how to get them from the server, outgoing attachments
 * have a file name and content).
 *
 * This interface will change as we learn more about attachments.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of September 1999
 */
public interface AttachmentInfo {
} // interfaceAttachmentInfo


Condition

Purpose: Represents conditions that might be used in filtering mail messages. You can select a field to filter on, an operator to apply, and a target. For example, you can select all messages whose sender includes the string "Joe" or all messages after a certain date. This is primarily a utility class for grouping the three aspects.

Methods:

/**
 * Construct a new condition.  More information is needed for the kinds of operators
 * that are allowed.
 */
public Condition(String fieldName, String operator, String target);

/**
 * Determine if the condition holds on a particular message.
 */
public boolean isRuleTrue(MailMessage msg);

Notes:

Folder

Purpose: Gathers a set of messages together. It's not clear how folders are used otherwise, and how you create them.

Methods:

/**
 * Delete a message.
 * @exception InvalidMessageNameException
 */
public ????? deleteMessage(String messageName)
  throws InvalidMessageNameException;

/**
 * Get the contents of a message.  It seems that this is only returns the body
 * and not the header.  Is this an appropriate design decision?
 * @exception InvalidMessageNameException.
 */
public String getMessage(String messageName)
  throws InvalidMessageNameException;

/**
 * Refile a message in a folder.
 * @exception InvalidMessageNameException
 * @exception InvalidFolderNameException
 */
public ????? fileMessage(String messageName, String folderName)
  throws InvalidFolderNameException, InvalidMessageNameException;

It would be nearly as clear, and much more concise, to use FolderException and MessageException.

Login

Purpose: Provides the infrastructure for logging in to the IMAP server. It appears that connections are two-step processes: first you connect to the server using openConnection then you log in using login. I do not know whether you need to create a Login first or what type each method returns.

Methods:

/**
 * Try to establish an IMAP connection with the given server.
 * @exception FailedConnectionException
 *   if it cannot connect.
 */
public ????? openConnection(String server, int port)
  throws FailedConnectionException;

/**
 * Try to log in to the server as a particular user.
 * @exception FailedLoginException
 *   if the login is invalid.
 */
public ????? login(String userName, String password)
  throws FailedLoginException;

Since most clients won't know the IMAP port of the server, I wonder about the requirement that they supply that port. However, that information might also be stored as part of the preferences system.

It might be easier (and shorter) to just call the exception ConnectionException and LoginException (just as NumberFormatException is used instead of IncorrectNumberFormatException). The ``Exception'' already indicates that it's an error.

Mailbox

Purpose: As we've discussed in class, the purpose of this class is to supply a mechanism by which clients can retrieve messages from the server. The Team responsible for this class have decided to separate the mailbox (which essentially stores Folders) from the messages (which you retrieve from folders).

Methods:

/**
 * List all the folders in the account.
 */
public ????? listFolders();

/**
 * Delete a complete folder and its contents.
 * @exception InvalidFolderNameException
 */
public ????? deleteFolder(String folderName)
  throws InvalidFolderNameException;

/**
 * Create a new, empty folder.
 */
public ????? createFolder(String folderName);

/**
 * List all the messages in a folder.
 * @exception InvalidFolderNameException
 */
public MailHeader[] getContents(String folderName)
  throws InvalidFolderNameException;
  
/** 
 * Rename a folder.
 * @exception InvalidFolderNameException
 */
public ????? renameFolder(String folderName, String newFolderName)
  throws InvalidFolderNameException;
  
/**
 * Closes the mailbox.
 */
public ????? bye();

I wonder how folders are to be used (or extracted), given that getContents already gets the contents. Do you ever need to set a current folder? Is it simply that the folder methods are being Teamed together? Since you connect with login, it might make more sense to disconnect with logout rather than bye. I'd also like more details on when these exceptions are thrown: what can go wrong?

MailMessage

Note: We've all worked on this class. This is my attempt to summarize and generalize our past work.


import AttachmentInfo;

/**
 * A simple representation of mail messages.  Note that the message identifier
 * is separate from the header; it is a value that the server might use
 * when returning messages.
 *
 * @author Samuel A. Rebelsky
 * @version 1.1 of October 1999
 */
public class MailMessage {
  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+
  
  /** 
   * The header of the message.  This specifies sender, recipient,
   * and so on and so forth.
   */
  protected MessageHeader header;
  
  /**
   * A unique identifier for the message.  This is typically generated
   * by the mail server so that clients can refer to messages by
   * identifier.
   */
  protected String identifier;
  
  /**
   * The body of the message.
   */
  protected String body;
  
  /**
   * Information on any attachments the message may have.  This is
   * always non-null.
   */
  protected AttachmentInfo[] attachments;
  
  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+
  
  /**
   * Build a complete mail message from one of the textual messages
   * typically sent across the Internet.  Also requires a local
   * identifier for the message.
   */
  public MailMessage(String message, String identifier) {
    // STUB.  To be implemented.
    this(identifier);
  } // MailMessage(String,String)
  
  /**
   * Build a new, empty, mail message.  Requires a local identifier
   * that uniquely identifies the message.
   */
  public MailMessage(String identifier) {
    this.identifier = identifier;
    this.body = "";
    this.header = new SampleMessageHeader();
    this.attachments = new AttachmentInfo[0];
  } // MailMessage(String)
  
  // +------------+----------------------------------------------
  // | Extractors |
  // +------------+
  
  /**
   * Get information on the nth attachment.  It is the responsibility of the 
   * client to then use that information to request the attachment from the
   * server.
   * @exception MessageException
   *   If there is no such attachment.
   */
  public AttachmentInfo getAttachmentInfo(int n) {
    if ((n < 0) || (n >= attachments.length)) {
      throw new MessageException("Request for attachment " + n + ", only 0 .." +
                                 attachments.length-1);
    } // if the message number is invalid.
    return attachments[n];
  } // getAttachment(int)
  
  /**
   * Get the body of the message.
   */
  public String getBody() {
    return this.body;
  } // getBody()
  
  /**
   * Get one field of the header.  Returns the empty string if the field
   * is not defined.
   */
  public String getField(String fieldName) {
    try {
      return this.header.getField(fieldName);
    }
    catch (FieldException fe) {
      return "";
    }
  } // getField(String)
  
  /**
   * Get the string used to uniquely identify this message.
   */
  public String getId() {
    return this.identifier;
  } // getId()
  
  /**
   * Convert the message to a string, for ease of printing.
   */
  public String toString() {
    return this.toString(this.header.toString());
  } // toString()
  
  /**
   * Convert the message to a string, using only selected fields in the header.
   */
  public String toString(String[] headerFields) {
    return this.toString(this.head.toString(headerFields));
  } // toString(String[])
  
  /**
   * Convert the message to a string, using the selected string as the header.
   * This is primarily intended as a utility class because the other two
   * versions of toString() are otherwise quite similar, and it seems silly
   * to repeat the code.
   */
  protected String toString(String headerString) {
    String message = headerString + "\n" + this.body;
    if (this.attachments != null) {
      message = message + "\nAttachments:\n";
      for (int i = 0; i < this.attachments.length; ++i) {
        message = message + "  " + this.attachments[i] + "\n";
      } // for
    } // if (this.attachments != null)
  } // toString()

} // class MailMessage


MessageHeader

Code:


/**
 * A simple representation of the headers of messages.  Rather than using a
 * setXXX and getXXX for each expected field (which limits the client to
 * prespecified fields), this class lets you set or get as many fields as
 * you'd like.  You can determine the names of the standard fields by looking at
 * standardFields and the names of the fields set in the current message
 * from activeFields.  Typically, headers will be created empty, and then filled
 * in with a lot of calls to setXXX or a call to parseHeader.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of October 1999
 */
public interface MessageHeader {
  // +------------+----------------------------------------------
  // | Extractors |
  // +------------+
  
  /**
   * Get a list of the names of fields that are used by the current message.
   */
  public String[] activeFields();
  
  /**
   * Extract one field from the header.  
   * @exception FieldException
   *   If the field is not defined.
   */
  public String getField(String fieldName)
    throws FieldException;
  
  /**
   * Get a list of the names of fields that are used by most messages.
   */
  public String[] standardFields();
  
  /** 
   * Convert the header to a printable string, useful for
   * printing or saving to a file.
   */
  public String toString();
  
  /**
   * Convert a selected set of fields to a printable string,
   * useful for printing or saving to a file.  If a field
   * is not defined, it is treated as the empty string.
   */
  public String toString(String[] fieldNames);
  
  
  // +-----------+-----------------------------------------------
  // | Modifiers |
  // +-----------+
  
  /**
   * Clear the header.  Afterwards, there are no fields with values in
   * the header.
   */
  public void clear();
  
  /**
   * Parse a header created by toString() or in the standard Internet
   * email format.  Fill in all the fields specified in that header.
   * If a field had a previous value and was not specified in the header,
   * it retains its value.  If a field had a previous value and is specified
   * in the header, it takes on the new value.  If a field did not have a
   * previous value and is specified in the header, it takes on that value.
   */
  public void parse(String head);
  
  /**
   * Restrict the header to a particular set of fields.  All other fields
   * are "deleted", marked as unused.  If we restrict the header to a particular
   * field, and that field is not set, it gets set to the empty string.
   */
  public void restrict(String[] fieldNames);
  
  /**
   * Set one field of the header.  Returns the old value of the field.
   * Returns null if the field was not previously set.
   */
  public String setField(String fieldName, String fieldValue);

} // interface MessageHeader


Sample Implementation:


import MessageHeader;

/**
 * A version of MessageHeader that can be used during testing.
 * Not particularly sophisticated.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of September 1999
 */
public class SampleMessageHeader
  implements MessageHeader
{
  // +------------+----------------------------------------------
  // | Extractors |
  // +------------+
  
  /**
   * Get a list of the names of fields that are used by the current message.
   */
  public String[] activeFields() {
     String[] fields = { "Subject", "From", "To" };
     return fields;
  } // activeFields()
  
  /**
   * Extract one field from the header.  
   * @exception FieldException
   *   If the field is not defined.
   */
  public String getField(String fieldName)
    throws FieldException
  {
    // Who cares about capitalization?
    fieldName = fieldName.toLowerCase();
    // Try the three main fields.
    if (fieldName.equals("subject")) return "Testing!";
    if (fieldName.equals("from")) return "rebelsky@math.grin.edu";
    if (fieldName.equals("to")) return "rebelsky@grinnell.edu";
    // Not one of the three main fields.  Crash and burn.
    throw new FieldException("No field '" + fieldName + "'");
  } // getField()

  /**
   * Get a list of the names of fields that are used by most messages.
   */
  public String[] standardFields() {
    String[] fields = { "Subject", "From", "To", "Date", "Cc" };
    return fields;
  } // standardFields()
  
  /** 
   * Convert the header to a printable string, useful for
   * printing or saving to a file.
   */
  public String toString() {
    return "From: Sam Rebelsky <rebelsky@math.grin.edu>\n"
        +  "To: Professor Rebelsky <rebelsky@grinnell.edu>\n"
        +  "Subject: Testing\n";
  } // toString()
  
  /**
   * Convert a selected set of fields to a printable string,
   * useful for printing or saving to a file.  If a field
   * is not defined, it is treated as the empty string.
   */
  public String toString(String[] fieldNames) {
    String result = "";
    for (int i = 0; i < fieldNames.length; ++i) {
      // Add the label
      result = result + fieldNames[i] + ": ";
      // Try to add the contents.  If there is no conent, add nothing.
      try { result = result + this.getField(fieldNames[i]); }
      catch (Exception e) { }
      // Add the carriage return.
      result = result + "\n";
    } // for
    return result;
  } // toString(String[])
  
  // +-----------+-----------------------------------------------
  // | Modifiers |
  // +-----------+
  
  /**
   * Clear the header.  Afterwards, there are no fields with values in
   * the header.
   */
  public void clear() {
    // Stub.  Do nothing.
  } // clear()
  
  /**
   * Parse a header created by toString() or in the standard Internet
   * email format.  Fill in all the fields specified in that header.
   * If a field had a previous value and was not specified in the header,
   * it retains its value.  If a field had a previous value and is specified
   * in the header, it takes on the new value.  If a field did not have a
   * previous value and is specified in the header, it takes on that value.
   */
  public void parse(String head) {
    // Stub.  Do nothing.
  } // parse(String)
  
  /**
   * Restrict the header to a particular set of fields.  All other fields
   * are "deleted", marked as unused.  If we restrict the header to a particular
   * field, and that field is not set, it gets set to the empty string.
   */
  public void restrict(String[] fieldNames) {
    // Stub.  Do nothing.
  } // restrict(String[])
  
  /**
   * Set one field of the header.  Returns the old value of the field.
   * Returns null if the field was not previously set.
   */
  public String setField(String fieldName, String fieldValue) {
    // Stub.  Do nothing.
    return "";
  } // setField(String,String)

} // class SampleMessageHeader


Options

Purpose: Provides a simple mechanism for keeping track of options.

Suggested Options:

Name in outgoing mail:
Line Width
Messages per page
Save outgoing mail?
View attachments in message:
Quote original message:
                If yes: Separator? Let the user choose the separator
Preview messages:
Attach signature?
Message Header: None  The None option does not display any header
                      information.  Basic displays the sender's and 
                      recipients' names, the date, and the subject. 
                Basic Basic displays the sender's and 
                      recipients' names, the date, and the subject. 
                Full  Full also displays routing information that is useful 
                      for tracing messages. 
Confirm Sent messages

Suggested Methods: Since the Team did not supply methods, I'm suggesting some.

/**
 * Get an option whose value is a number.  For now, use integers as
 * our numbers.
 * @exception OptionException
 *   If that option is undefined.
 */
public int getNumericOption(String optionName) 
  throws OptionException;

/**
 * Set an option whose value is a number.  Again, use integers as our
 * numbers.  Discards the old value.
 */
public void setNumericOption(String optionName, int newValue);

/**
 * Get a flag: something that can either be on (true) or off (false).
 * @exception OptionException
 *   if that option is undefined.
 */
public boolean getFlag(String flagName)
  throws OptionException;

/**
 * Set a flag.
 */
public void setFlag(String flagName, boolean flagValue);

/**
 * Get an option whose value is a string.
 * @exception OptionException
 *   If that option is undefined.
 */
public String getOption(String optionName);

/**
 * Set an option whose value is a string.
 */
public void setOption(String optionName, String newValue);

/**
 * Get an option whose value is some other object.
 * @exception OptionException
 *   If that option is undefined.
 */
public Object getOption(String optionName);

/**
 * Set an option whose value is some object.
 */
public void setOption(String optionName, Object newValue);

Profile

Purpose: Stores information on a person.

Fields:

Name:
           First
           Last
Address    Street
           Street
           City
           State/Province
           zip/Postal Code
           Country
           Telephone (home)
           Telephone (work)
           E-mail Optional
Gender
Date of Birth
Include name in mailing list?
Signature:
A textfield to hold signatures

Methods: Unknown.

Rule

Purpose: Joins two parts: a condition and a destination folder which seems to hold the messages for which messages are filtered.

Methods:

/**
 * Build a new rule with a particular condition.
 */
public Rule(Condition cond, Folder destination);

/**
 * Convert the rule to a string so that it can be previed.
 */
public String toString();

/**
 * Do the filtering.
 */
public ????? scanList(MessageInfoList messages);

/**
 * Build a new folder from the filtering.  It's not clear how this differs
 * from scanList.
 */
public Folder returnFolder();

/**
 * Build a new list of messages from the filtering.  It's not clear how this
 * differs from the previous two methods.
 */
public MessageInfoList returnList();

Notes:

History

Sunday, 3 October 1999

Monday, 4 October 1999


Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/99F/Handouts/proposals.01.html

Source text last modified Mon Oct 4 09:29:12 1999.

This page generated on Mon Oct 4 09:29:50 1999 by Siteweaver. Validate this page's HTML.

Contact our webmaster at rebelsky@grinnell.edu