.

cmurphy [2008-04-03 21:15:27]
.
Filename
retina/chat/client/AuthenticationManager.java
retina/chat/client/ChatClient.java
retina/chat/client/ChatManager.java
retina/chat/client/ChatWindow.java
retina/chat/client/GroupManager.java
retina/chat/client/GroupWindow.java
retina/chat/client/MessageManager.java
retina/chat/client/NotMemberOfGroupException.java
retina/chat/client/Preferences.java
retina/chat/client/PreferencesWindow.java
retina/chat/client/ScrollTest.java
retina/chat/client/ScrollWindow.java
retina/chat/client/TestServer.java
retina/chat/common/Packet.java
retina/chat/server/ChatServer.java
retina/chat/server/Group.java
retina/chat/server/Handler.java
retina/chat/server/Message.java
retina/chat/server/MessageWriter.java
retina/chat/server/ServerSocketListenerThread.java
retina/chat/server/User.java
retina/chat/server/UserSocketListenerThread.java
retina/common/CompilationErrorEvent.java
retina/common/CompilationEvent.java
retina/common/Event.java
retina/common/Logger.java
retina/common/OccurrenceMap.java
retina/common/Student.java
retina/db/CompilationErrorEventManager.java
retina/db/CompilationEventManager.java
retina/db/DatabaseConnector.java
retina/db/DatabaseManager.java
retina/db/DatabaseReader.java
retina/db/DatabaseWriter.java
retina/db/LoginEventManager.java
retina/db/LoginEventServer.java
retina/db/RetinaServer.java
retina/db/XMLLoader.java
retina/db/XmlServer.java
retina/ui/AssignmentPane.java
retina/ui/BrowsePane.java
retina/ui/DBTableModel.java
retina/ui/DateConverter.java
retina/ui/ErrorTimeGraph.java
retina/ui/MainPane.java
retina/ui/StatGraph.java
retina/ui/UserInterfaceManager.java
diff --git a/retina/chat/client/AuthenticationManager.java b/retina/chat/client/AuthenticationManager.java
new file mode 100644
index 0000000..b6fe148
--- /dev/null
+++ b/retina/chat/client/AuthenticationManager.java
@@ -0,0 +1,105 @@
+package retina.chat.client;
+
+import java.awt.Cursor;
+import java.util.Vector;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import retina.chat.common.Packet;
+
+
+public class AuthenticationManager
+{
+    private ChatClient parent;
+    private String username;
+
+    /** Creates a new instance of SignInManager */
+    public AuthenticationManager(ChatClient chatClient)
+    {
+        parent = chatClient;
+    }
+
+    public void signIn(String username)
+    {
+        if (username == null)
+        {
+            // the user clicked "cancel"
+            System.exit(0);
+        }
+
+        while (username.length() == 0)
+        {
+            username = JOptionPane.showInputDialog(null, "The username must not be blank!\nPlease enter your username:", "Welcome to Lederhosen 2.0", JOptionPane.PLAIN_MESSAGE);
+            if (username == null)
+            {
+                // the user clicked "cancel"
+                System.exit(0);
+            }
+        }
+
+        this.username = username;
+
+        Packet packet = new Packet(Packet.MSGTYPE_SIGNON);
+        packet.setUsername(username);
+        boolean sendMessageStatus = parent.getMessageManager().sendMessage(packet);
+
+        if (!sendMessageStatus)
+        {
+            JOptionPane.showMessageDialog(null, "Could not connect to the server to try to sign in.\nPlease try again later.", "Error", JOptionPane.ERROR_MESSAGE);
+            System.exit(0);
+        }
+    }
+
+    public void handleSignInMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            username = JOptionPane.showInputDialog(null, "An error occurred while communicating with the server. Please try to sign in again:", "Welcome to Lederhosen 2.0", JOptionPane.ERROR_MESSAGE);
+            signIn(username);
+            return;
+        }
+
+        Vector groupList = packet.getDataVector();
+        int statusCode = packet.getStatusCode();
+
+
+        if (statusCode == Packet.ERROR_USER_EXISTS)
+        {
+            username = JOptionPane.showInputDialog(null, "A user with that name already exists. Please enter a different username:", "Welcome to Lederhosen 2.0", JOptionPane.ERROR_MESSAGE);
+            signIn(username);
+        }
+        else if (statusCode == Packet.SUCCESS)
+        {
+            parent.getGroupManager().setGroupList(groupList);
+            parent.getGroupManager().showGroupWindow();
+        }
+        else
+        {
+            // something else happened...
+            JOptionPane.showMessageDialog(null, "An unknown error occurred! Incorrect status code returned from server.", "Error", JOptionPane.ERROR_MESSAGE);
+            System.exit(0);
+        }
+
+    }
+
+    public void signOut()
+    {
+        Packet packet = new Packet(Packet.MSGTYPE_SIGN_OUT);
+        parent.getMessageManager().sendMessage(packet);
+
+        // don't forget to clean up!!
+        parent.getMessageManager().disconnect();
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    /*
+     * This method is only called by test code.
+     */
+    public void setUsername(String name)
+    {
+        this.username = name;
+    }
+}
diff --git a/retina/chat/client/ChatClient.java b/retina/chat/client/ChatClient.java
new file mode 100644
index 0000000..297a277
--- /dev/null
+++ b/retina/chat/client/ChatClient.java
@@ -0,0 +1,112 @@
+package retina.chat.client;
+
+import java.awt.Cursor;
+import javax.swing.*;
+import javax.swing.JFrame;
+
+
+public class ChatClient
+{
+    private AuthenticationManager authenticationManager;
+    private GroupManager groupManager;
+    private ChatManager chatManager;
+    private MessageManager messageManager;
+    private String username;
+
+    private static final String DEFAULT_HOST = "localhost";
+    private static final int DEFAULT_PORT = 4444;
+
+    /** Creates a new instance of Main
+     *  serverMachineName is a domain name or IP address.
+     */
+    public ChatClient(String serverMachineName, int serverPortNumber)
+    {
+        authenticationManager = new AuthenticationManager(this);
+        groupManager = new GroupManager(this);
+        chatManager = new ChatManager(this);
+        messageManager = new MessageManager(this, serverMachineName, serverPortNumber);
+    }
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args)
+    {
+        String usageStatement = "Usage: java ChatClient SERVER_NAME SERVER_PORT";
+
+        /*
+        if (args.length != 2)
+        {
+            System.out.println(usageStatement);
+            System.out.println("SERVER_NAME: IP Address or domain name of machine where chat server is running.");
+            System.out.println("SERVER_NAME: Port of machine where chat server is running.");
+            System.exit(0);
+        }
+        */
+
+        String serverMachineName = DEFAULT_HOST;
+        int serverPortNumber = DEFAULT_PORT;
+
+        if (args.length >= 2)
+        {
+        	serverMachineName = args[0];
+
+        	serverPortNumber = -1;
+        	try
+        	{
+        		serverPortNumber = Integer.parseInt(args[1]);
+        	}
+        	catch(NumberFormatException e)
+        	{
+        		System.out.println(usageStatement);
+        		System.out.println("The SERVER_PORT must be an integer.");
+        		System.exit(1);
+        	}
+        }
+
+        ChatClient client = new ChatClient(serverMachineName, serverPortNumber);
+        client.start();
+    }
+
+    public void finalize()
+    {
+        System.out.println("ChatClient has been garbage-collected.");
+    }
+
+    public void start()
+    {
+        if (!messageManager.connect())
+        {
+            JOptionPane.showMessageDialog(null, "A connection could not be made to the server!", "Error", JOptionPane.ERROR_MESSAGE);
+            System.exit(0);
+        }
+
+        // start the new thread
+        messageManager.start();
+
+        username = JOptionPane.showInputDialog(null, "Please enter your username:", "Welcome to Retina", JOptionPane.PLAIN_MESSAGE);
+        // System.out.println("username: " + username);
+        authenticationManager.signIn(username);
+    }
+
+
+    public GroupManager getGroupManager()
+    {
+        return groupManager;
+    }
+
+    public AuthenticationManager getAuthenticationManager()
+    {
+        return authenticationManager;
+    }
+
+    public ChatManager getChatManager()
+    {
+        return chatManager;
+    }
+
+    public MessageManager getMessageManager()
+    {
+        return messageManager;
+    }
+}
diff --git a/retina/chat/client/ChatManager.java b/retina/chat/client/ChatManager.java
new file mode 100644
index 0000000..cf8eecb
--- /dev/null
+++ b/retina/chat/client/ChatManager.java
@@ -0,0 +1,133 @@
+package retina.chat.client;
+
+import java.util.Hashtable;
+import javax.swing.JOptionPane;
+
+import retina.chat.common.Packet;
+
+public class ChatManager
+{
+
+    private ChatClient parent;
+
+    /**
+     * key: groupname (String)
+     * value: ChatWindow
+     */
+    private Hashtable chatWindows;
+
+    /** Creates a new instance of ChatManager */
+    public ChatManager(ChatClient chatClient)
+    {
+        parent = chatClient;
+        chatWindows = new Hashtable();
+    }
+
+    public void sendChatMessage(String groupname, String chatMessage, ChatWindow chatWindow)
+    {
+        if (groupname == null || chatMessage == null || chatWindow == null)
+        {
+            JOptionPane.showMessageDialog(null, "An internal error occurred while trying to send the message.\nPlease try again later.", "Error", JOptionPane.ERROR_MESSAGE);
+            // put back the message...
+            chatWindow.setTextField(chatMessage);
+        }
+
+	/**
+	// see what type of message it is - default to regular old "chat message"
+	int type = Packet.MSGTYPE_CHAT_MESSAGE;
+
+	// the text itself is inside an HTML tag, so to evaluate it we need to strip it out
+	System.out.println(chatMessage);
+	if (chatMessage.contains("help"))
+	    {
+		type = Packet.MSGTYPE_HELP;
+		System.out.println("HELP MESSAGE");
+	    }
+	else System.out.println("REGULAR MESSAGE");
+	**/
+
+        Packet packet = new Packet(Packet.MSGTYPE_CHAT_MESSAGE);
+        packet.setGroupname(groupname);
+        packet.setChatMessage(chatMessage);
+        boolean sendMessageStatus = parent.getMessageManager().sendMessage(packet);
+
+        if (!sendMessageStatus)
+        {
+            JOptionPane.showMessageDialog(null, "Could not connect to the server to send message.\nPlease try again later.", "Error", JOptionPane.ERROR_MESSAGE);
+            // put back the message...
+            chatWindow.setTextField(chatMessage);
+        }
+    }
+
+
+    public void leaveGroup(String groupname)
+    {
+        if (groupname == null)
+        {
+            return;
+        }
+        Packet packet = new Packet(Packet.MSGTYPE_LEAVE_GROUP);
+        packet.setGroupname(groupname);
+        parent.getMessageManager().sendMessage(packet);
+    }
+
+    /**
+     * This code is called asynchronously when it receives a message from the server.
+     */
+    public void handleReceiveChatMessageMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user sent a chat message but it could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        String groupname = packet.getGroupname();
+        String username = packet.getUsername();
+        String chatMessage = packet.getChatMessage();
+
+        if (groupname == null || username == null || chatMessage == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user sent a chat message but it could not be fully read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (!chatWindows.containsKey(groupname))
+        {
+            return;
+        }
+
+        ChatWindow chatWindow = (ChatWindow)chatWindows.get(groupname);
+        chatWindow.appendChatMessageToChatPanel(username, chatMessage);
+    }
+
+    public void createChatWindow(String groupname)
+    {
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while creating the chat window. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        ChatWindow window = new ChatWindow(parent, groupname);
+        chatWindows.put(groupname, window);
+        window.setVisible(true);
+    }
+
+    public void removeChatWindow(String groupname)
+    {
+        if (groupname != null && chatWindows.containsKey(groupname))
+        {
+            chatWindows.remove(groupname);
+        }
+    }
+
+    public ChatWindow getChatWindow(String groupname)
+    {
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while receiving a chat message. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return null;
+        }
+
+        return (ChatWindow)(chatWindows.get(groupname));
+    }
+}
diff --git a/retina/chat/client/ChatWindow.java b/retina/chat/client/ChatWindow.java
new file mode 100644
index 0000000..a126894
--- /dev/null
+++ b/retina/chat/client/ChatWindow.java
@@ -0,0 +1,302 @@
+
+package retina.chat.client;
+
+import java.awt.event.KeyEvent;
+import java.util.Vector;
+import javax.swing.JOptionPane;
+
+
+public class ChatWindow extends javax.swing.JFrame
+{
+    private ChatClient parent;
+
+    private String groupname;
+
+    private String conversation = "";
+
+    private Preferences preferences;
+    private PreferencesWindow preferencesWindow;
+
+    /** Creates new form ChatWindow */
+    public ChatWindow(ChatClient parent, String groupname)
+    {
+        initComponents();
+        this.parent = parent;
+        this.groupname = groupname;
+        setTitle("Retina: " + groupname);
+
+        // put the window in the middle of the screen
+        setLocationRelativeTo(null);
+
+        preferences = new Preferences(Preferences.BLACK, Preferences.MEDIUM);
+        preferencesWindow = new PreferencesWindow(this);
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jPanel1 = new javax.swing.JPanel();
+        messageTextField = new javax.swing.JTextField();
+        sendMessageButton = new javax.swing.JButton();
+        jSplitPane1 = new javax.swing.JSplitPane();
+        chatPanelScrollPane = new javax.swing.JScrollPane();
+        chatPanel = new javax.swing.JEditorPane();
+        memberListPanelScrollPane = new javax.swing.JScrollPane();
+        memberListPanel = new javax.swing.JTextArea();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        addKeyListener(new java.awt.event.KeyAdapter()
+        {
+            public void keyPressed(java.awt.event.KeyEvent evt)
+            {
+                handleKeyReleased(evt);
+            }
+        });
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosed(java.awt.event.WindowEvent evt)
+            {
+                windowClosedHandler(evt);
+            }
+        });
+
+        jPanel1.setLayout(new java.awt.BorderLayout());
+
+        messageTextField.setText("Please type your message here");
+        messageTextField.addKeyListener(new java.awt.event.KeyAdapter()
+        {
+            public void keyReleased(java.awt.event.KeyEvent evt)
+            {
+                handleKeyReleased(evt);
+            }
+        });
+        messageTextField.addMouseListener(new java.awt.event.MouseAdapter()
+        {
+            public void mouseClicked(java.awt.event.MouseEvent evt)
+            {
+                handleMouseClicked(evt);
+            }
+        });
+
+        jPanel1.add(messageTextField, java.awt.BorderLayout.CENTER);
+
+        sendMessageButton.setText("Send Message");
+        sendMessageButton.setEnabled(false);
+        sendMessageButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                sendMessageHandler(evt);
+            }
+        });
+
+        jPanel1.add(sendMessageButton, java.awt.BorderLayout.EAST);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.SOUTH);
+
+        jSplitPane1.setMinimumSize(new java.awt.Dimension(400, 200));
+        jSplitPane1.setPreferredSize(new java.awt.Dimension(400, 200));
+        chatPanelScrollPane.setAutoscrolls(true);
+        chatPanelScrollPane.setPreferredSize(new java.awt.Dimension(303, 200));
+        chatPanel.setEditable(false);
+        chatPanel.setContentType("text/html");
+        chatPanel.setMinimumSize(new java.awt.Dimension(300, 200));
+        chatPanelScrollPane.setViewportView(chatPanel);
+
+        jSplitPane1.setLeftComponent(chatPanelScrollPane);
+
+        memberListPanel.setEditable(false);
+        memberListPanelScrollPane.setViewportView(memberListPanel);
+
+        jSplitPane1.setRightComponent(memberListPanelScrollPane);
+
+        getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);
+
+        pack();
+    }//GEN-END:initComponents
+
+
+    private boolean started = false;
+    private void handleMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_handleMouseClicked
+    {//GEN-HEADEREND:event_handleMouseClicked
+        checkIfAnyText();
+        if (!started)
+        {
+            messageTextField.setText("");
+            started = true;
+        }
+    }//GEN-LAST:event_handleMouseClicked
+
+    private void handleKeyReleased(java.awt.event.KeyEvent evt)//GEN-FIRST:event_handleKeyReleased
+    {//GEN-HEADEREND:event_handleKeyReleased
+        checkIfAnyText();
+
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+            sendMessageHandler(null); // it's okay to send null because the event isn't being used
+        }
+        else if (evt.getKeyCode() == KeyEvent.VK_F7)
+        {
+            messageTextField.setText("WE ROCK!");
+            sendMessageButton.setEnabled(true);
+        }
+        else if (evt.getKeyCode() == KeyEvent.VK_F2)
+        {
+            preferencesWindow.setVisible(true);
+        }
+    }//GEN-LAST:event_handleKeyReleased
+
+    private void checkIfAnyText()
+    {
+        String message = messageTextField.getText();
+        if (message != null && message.length() > 0)
+        {
+            sendMessageButton.setEnabled(true);
+        }
+        else
+        {
+            sendMessageButton.setEnabled(false);
+        }
+    }
+
+    private void windowClosedHandler(java.awt.event.WindowEvent evt)//GEN-FIRST:event_windowClosedHandler
+    {//GEN-HEADEREND:event_windowClosedHandler
+        parent.getChatManager().leaveGroup(groupname);
+
+        parent.getGroupManager().removeFromGroup(groupname);
+        parent.getChatManager().removeChatWindow(groupname);
+    }//GEN-LAST:event_windowClosedHandler
+
+    private void sendMessageHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_sendMessageHandler
+    {//GEN-HEADEREND:event_sendMessageHandler
+        String message = messageTextField.getText();
+
+        if (message != null && message.length() > 0)
+        {
+            String size = "4";
+            if (preferences.getSize().equals(Preferences.SMALL)) size = "-1";
+            else if (preferences.getSize().equals(Preferences.LARGE)) size = "+1";
+            else if (preferences.getSize().equals(Preferences.EXTRA_LARGE)) size = "+2";
+            message = "<font size=\"" + size + "\" color=" + preferences.getColor() + ">" + message + "</font>";
+
+            parent.getChatManager().sendChatMessage(groupname, message, this);
+        }
+
+        //TESTING
+        //refreshWindow();
+
+        messageTextField.setText("");
+        sendMessageButton.setEnabled(false);
+    }//GEN-LAST:event_sendMessageHandler
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+            public void run()
+            {
+                String serverName = "localhost";
+                int serverPortNumber = 4444;
+                ChatClient parent = new ChatClient(serverName, serverPortNumber);
+                new ChatWindow(parent, "Test Group").setVisible(true);
+            }
+        });
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JEditorPane chatPanel;
+    private javax.swing.JScrollPane chatPanelScrollPane;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JSplitPane jSplitPane1;
+    private javax.swing.JTextArea memberListPanel;
+    private javax.swing.JScrollPane memberListPanelScrollPane;
+    private javax.swing.JTextField messageTextField;
+    private javax.swing.JButton sendMessageButton;
+    // End of variables declaration//GEN-END:variables
+
+    public void appendChatMessageToChatPanel(String username, String message)
+    {
+        if (username == null || message == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user sent a chat message but it could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        appendTextToChatPanel("<b>" + username + ":</b> " + message);
+    }
+
+    private void appendTextToChatPanel(String message)
+    {
+        if (message == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user sent a chat message but it could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        conversation += message + "<br>";
+        chatPanel.setText(conversation);
+    }
+
+    public void announceMemberArrival(String username)
+    {
+        if (username == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user has joined the group but the name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        appendTextToChatPanel("<i><b>" + username + "</b> has joined the chat group.</i>");
+    }
+
+    public void announceMemberDeparture(String username)
+    {
+        if (username == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server. Another user has left the group but the name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        appendTextToChatPanel("<i><b>" + username + "</b> has left the chat group.</i>");
+    }
+
+    public void setTextField(String text)
+    {
+        if (text == null)
+        {
+            text = "";
+        }
+
+        messageTextField.setText(text);
+    }
+
+    public void updateGroupMemberListPanel(Vector memberList)
+    {
+        memberListPanel.setText("");
+        if (memberList == null)
+        {
+            memberListPanel.append(parent.getAuthenticationManager().getUsername());
+        }
+        else
+        {
+            for (int i = 0; i < memberList.size(); i++)
+            {
+                memberListPanel.append((String)(memberList.get(i)));
+                memberListPanel.append("\n");
+            }
+        }
+    }
+
+    public String getGroupname()
+    {
+        return groupname;
+    }
+
+    public Preferences getPreferences()
+    {
+        return preferences;
+    }
+}
diff --git a/retina/chat/client/GroupManager.java b/retina/chat/client/GroupManager.java
new file mode 100644
index 0000000..18567d2
--- /dev/null
+++ b/retina/chat/client/GroupManager.java
@@ -0,0 +1,386 @@
+package retina.chat.client;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.swing.JOptionPane;
+
+import retina.chat.common.Packet;
+
+public class GroupManager
+{
+    private ChatClient parent;
+
+    private Vector groupList;
+
+    private GroupWindow groupWindow;
+
+    /*
+     * key: groupname
+     * value: Vector of usernames of members of the group
+     */
+    private Hashtable groupMemberLists; // for groups that the user is in
+
+    /** Creates a new instance of GroupManager */
+    public GroupManager(ChatClient chatClient)
+    {
+        parent = chatClient;
+        groupMemberLists = new Hashtable();
+        groupList = new Vector();
+        groupWindow = new GroupWindow(parent);
+    }
+
+
+    public void createAndJoinNewGroup(String groupname)
+    {
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while creating a new group. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (groupList.contains(groupname))
+        {
+            JOptionPane.showMessageDialog(null, "A group named '" + groupname + "' already exists.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        Packet packet = new Packet(Packet.MSGTYPE_CREATE_NEW_GROUP);
+        packet.setGroupname(groupname);
+        boolean messageSentSuccessfully = parent.getMessageManager().sendMessage(packet);
+
+        if (!messageSentSuccessfully)
+        {
+            JOptionPane.showMessageDialog(null, "Could not connect to the server to create new group.\nPlease try again later.", "Error", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    public void handleCreateAndJoinNewGroupMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nThe new group could not be created.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nThe new group could not be created because the name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        int statusCode = packet.getStatusCode();
+
+        if (statusCode == Packet.SUCCESS)
+        {
+            Vector memberList = new Vector();
+//            System.out.println("username:" + parent.getAuthenticationManager().getUsername() + ", groupname: " + groupname);
+	    String username = parent.getAuthenticationManager().getUsername();
+	    if (username != null)
+	    {
+		memberList.add(username);
+	    }
+
+	    if (!groupMemberLists.containsKey(groupname))
+	    {
+		groupMemberLists.put(groupname, memberList);
+	    }
+
+            if (!groupList.contains(groupname))
+	    {
+		groupList.add(groupname);
+	    }
+
+            // now we open the new group
+	    ChatManager chatManager = parent.getChatManager();
+            chatManager.createChatWindow(groupname);
+            chatManager.getChatWindow(groupname).updateGroupMemberListPanel(memberList);
+
+            // and update the list in the group window
+            if (groupWindow != null)
+	    {
+		groupWindow.updateGroupList();
+	    }
+	    else
+	    {
+		System.out.println("Could not update the group list. The groupWindow is null.");
+	    }
+        }
+        else if (statusCode == Packet.ERROR_GROUP_EXISTS)
+        {
+            // group was created in the short time between when we pressed the create group button and when we pressed submit
+            JOptionPane.showMessageDialog(groupWindow, "A group named '" + groupname + "' already exists.", "Error", JOptionPane.ERROR_MESSAGE);
+        }
+        else
+        {
+            JOptionPane.showMessageDialog(groupWindow, "An unexpected error occurred. Received packet with status code " + statusCode, "Error", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    public Vector getGroupList()
+    {
+        return groupList;
+    }
+
+    public void setGroupList(Vector groupList)
+    {
+        if (groupList == null)
+        {
+            return;
+        }
+
+        this.groupList = groupList;
+    }
+
+    /**
+     * This code is called asynchronously when it receives a message from the server.
+     */
+    public void handleUpdateGroupListWithNewGroupMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nA new group was created but the message could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nA new group was created but the group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (!groupList.contains(groupname))
+        {
+            groupList.add(groupname);
+
+            // and update the list in the group window
+            groupWindow.updateGroupList();
+        }
+    }
+
+    /**
+     * This code is called asynchronously when it receives a message from the server.
+     */
+    public void handleUpdateGroupListRemoveGroupMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nA group was removed but the message could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nA group was removed but the group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (groupList.contains(groupname))
+        {
+            groupList.remove(groupname);
+
+            // and update the list in the group window
+            groupWindow.updateGroupList();
+        }
+    }
+
+    public void joinGroup(String groupname)
+    {
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while joining the group. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (groupMemberLists.containsKey(groupname))
+        {
+            JOptionPane.showMessageDialog(groupWindow, "An error occurred: You are already a member of the group.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        Packet packet = new Packet(Packet.MSGTYPE_JOIN_GROUP);
+        packet.setGroupname(groupname);
+        boolean messageSentSuccessfully = parent.getMessageManager().sendMessage(packet);
+
+        if (!messageSentSuccessfully)
+        {
+            JOptionPane.showMessageDialog(null, "Could not connect to the server to create new group.\nPlease try again later.", "Error", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    public void handleJoinGroupMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nThe application was unable to join you into this group.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while joining the group. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        int statusCode = packet.getStatusCode();
+
+        if (statusCode == Packet.SUCCESS)
+        {
+	    Vector memberList = packet.getDataVector();
+            groupMemberLists.put(groupname, memberList);
+	    ChatManager chatManager = parent.getChatManager();
+            chatManager.createChatWindow(groupname);
+            chatManager.getChatWindow(groupname).updateGroupMemberListPanel(memberList);
+        }
+        else if (statusCode == Packet.ERROR_ALREADY_IN_GROUP)
+        {
+            JOptionPane.showMessageDialog(groupWindow, "An error occurred: You are already a member of the group.", "Error", JOptionPane.ERROR_MESSAGE);
+        }
+        else if (statusCode == Packet.ERROR_GROUP_DOES_NOT_EXIST)
+        {
+            JOptionPane.showMessageDialog(groupWindow, "An error occurred: The group named '" + groupname + "' no longer exists.", "Error", JOptionPane.ERROR_MESSAGE);
+        }
+        else
+        {
+            JOptionPane.showMessageDialog(groupWindow, "An unexpected error occurred. Received status code " + statusCode, "Error", JOptionPane.ERROR_MESSAGE);
+        }
+
+    }
+
+    public void removeFromGroup(String groupname)
+    {
+        if (groupname == null)
+        {
+            return;
+        }
+
+        if (groupMemberLists.containsKey(groupname))
+        {
+            groupMemberLists.remove(groupname);
+        }
+
+    }
+
+    public Vector getGroupMembers(String groupname) throws NotMemberOfGroupException
+    {
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while getting the members of the group. The group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return null;
+        }
+
+        if (groupMemberLists.containsKey(groupname))
+        {
+            Vector members = (Vector)groupMemberLists.get(groupname);
+            return members;
+        }
+        else
+        {
+            throw new NotMemberOfGroupException();
+        }
+    }
+
+    /**
+     * This method is called asynchronously.
+     */
+    public void handleUpdateGroupMemberListWithNewMemberMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has joined the group but the information could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has joined a group but the group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String username = packet.getUsername();
+
+        if (username == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has joined the group but the user name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (groupMemberLists.containsKey(groupname))
+        {
+            Vector members = (Vector)groupMemberLists.get(groupname);
+            members.add(username);
+
+            ChatWindow chatWindow = parent.getChatManager().getChatWindow(groupname);
+	    if (chatWindow != null)
+	    {
+		chatWindow.updateGroupMemberListPanel(members);
+		chatWindow.announceMemberArrival(username);
+	    }
+	    else
+	    {
+		System.out.println("An error occurred: Could not update the chat window because chatWindow is null.");
+	    }
+        }
+    }
+
+    /**
+     * This method is called asynchronously.
+     */
+    public void handleUpdateGroupMemberListRemoveMemberMessage(Packet packet)
+    {
+        if (packet == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has left the group but the information could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = packet.getGroupname();
+
+        if (groupname == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has left a group but the group name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String username = packet.getUsername();
+
+        if (username == null)
+        {
+            JOptionPane.showMessageDialog(null, "An error occurred while communicating with the server.\nAnother user has left the group but the user name could not be read.", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        if (groupMemberLists.containsKey(groupname))
+        {
+            Vector members = (Vector)groupMemberLists.get(groupname);
+            members.remove(username);
+
+            ChatWindow chatWindow = parent.getChatManager().getChatWindow(groupname);
+	    if (chatWindow != null)
+	    {
+		chatWindow.updateGroupMemberListPanel(members);
+		chatWindow.announceMemberDeparture(username);
+	    }
+	    else
+	    {
+		System.out.println("An error occurred: Could not update the chat window because chatWindow is null.");
+	    }
+        }
+    }
+
+    public void showGroupWindow()
+    {
+        groupWindow.updateGroupList();
+        groupWindow.setVisible(true);
+    }
+ }
diff --git a/retina/chat/client/GroupWindow.java b/retina/chat/client/GroupWindow.java
new file mode 100644
index 0000000..11895b7
--- /dev/null
+++ b/retina/chat/client/GroupWindow.java
@@ -0,0 +1,189 @@
+
+package retina.chat.client;
+
+import java.awt.Dimension;
+import java.util.Vector;
+import javax.swing.JOptionPane;
+
+public class GroupWindow extends javax.swing.JFrame
+{
+
+    private ChatClient parent;
+
+    /** Creates new form GroupWindow */
+    public GroupWindow(ChatClient parent)
+    {
+        this.parent = parent;
+        initComponents();
+//        updateGroupList();
+
+        // put the window in the middle of the screen
+        setLocationRelativeTo(null);
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jPanel4 = new javax.swing.JPanel();
+        jButton1 = new javax.swing.JButton();
+        jButton2 = new javax.swing.JButton();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jList1 = new javax.swing.JList();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("Retina: Choose a Group");
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosed(java.awt.event.WindowEvent evt)
+            {
+                windowClosedHandler(evt);
+            }
+        });
+
+        jButton1.setText("Join Group");
+        jButton1.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                joinGroupHandler(evt);
+            }
+        });
+
+        jPanel4.add(jButton1);
+
+        jButton2.setText("Create Group");
+        jButton2.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                newGroupHandler(evt);
+            }
+        });
+
+        jPanel4.add(jButton2);
+
+        getContentPane().add(jPanel4, java.awt.BorderLayout.SOUTH);
+
+        jScrollPane1.setPreferredSize(new java.awt.Dimension(310, 110));
+        jList1.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)));
+        jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+        jList1.setEnabled(false);
+        jList1.setPreferredSize(new java.awt.Dimension(300, 100));
+        jScrollPane1.setViewportView(jList1);
+
+        getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
+
+        pack();
+    }//GEN-END:initComponents
+
+    private void windowClosedHandler(java.awt.event.WindowEvent evt)//GEN-FIRST:event_windowClosedHandler
+    {//GEN-HEADEREND:event_windowClosedHandler
+        parent.getAuthenticationManager().signOut();
+        System.exit(0);
+    }//GEN-LAST:event_windowClosedHandler
+
+//    int counter = 0;
+    private void newGroupHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_newGroupHandler
+    {//GEN-HEADEREND:event_newGroupHandler
+        // show the dialog to create the new group
+        String groupname = JOptionPane.showInputDialog(null, "Please enter the name of the new group you want to create:", "Create Group", JOptionPane.PLAIN_MESSAGE);
+        if (groupname == null)
+            return;
+
+        while (groupname.length() == 0)
+        {
+            groupname = JOptionPane.showInputDialog(null, "Error: group name cannot be blank.\nPlease enter the name of the new group you want to create:", "Create Group", JOptionPane.ERROR_MESSAGE);
+            if (groupname == null)
+                return;
+        }
+
+        parent.getGroupManager().createAndJoinNewGroup(groupname);
+
+//        Vector groupList = parent.getGroupManager().getGroupList();
+//        String newGroupName = "group" + counter++;
+//        groupList.add(newGroupName);
+//        updateGroupList();
+    }//GEN-LAST:event_newGroupHandler
+
+    private void joinGroupHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_joinGroupHandler
+    {//GEN-HEADEREND:event_joinGroupHandler
+        if (parent.getGroupManager().getGroupList().isEmpty())
+        {
+            // there are no groups to join
+            return;
+        }
+
+        // need to figure out which group was selected
+        if (jList1.getSelectedValue() == null || jList1.getSelectedValue().toString() == null)
+        {
+            JOptionPane.showMessageDialog(this, "You must select the name of a group to join", "Error", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+
+        String groupname = jList1.getSelectedValue().toString();
+
+        parent.getGroupManager().joinGroup(groupname);
+
+//        Vector groupList = parent.getGroupManager().getGroupList();
+//        groupList.remove(0);
+//        updateGroupList();
+    }//GEN-LAST:event_joinGroupHandler
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+            public void run()
+            {
+                String serverName = "localhost";
+                int serverPortNumber = 4444;
+                ChatClient parent = new ChatClient(serverName, serverPortNumber);
+                new GroupWindow(parent).setVisible(true);
+            }
+        });
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JButton jButton2;
+    private javax.swing.JList jList1;
+    private javax.swing.JPanel jPanel4;
+    private javax.swing.JScrollPane jScrollPane1;
+    // End of variables declaration//GEN-END:variables
+
+    public void updateGroupList()
+    {
+        Vector groupList = parent.getGroupManager().getGroupList();
+
+        if (groupList == null || groupList.isEmpty())
+        {
+            groupList = new Vector();
+            String empty = "There currently are no groups";
+            groupList.add(empty);
+            jList1.setEnabled(false);
+        }
+        else
+        {
+            jList1.setEnabled(true);
+        }
+        jList1.setListData(groupList);
+
+        if (groupList.size() > 5)
+        {
+            int estimatedRowHeightInPixels = 20;
+            jList1.setPreferredSize(new Dimension(300, groupList.size() * estimatedRowHeightInPixels));
+        }
+        else
+        {
+            int originalWindowHeight = 100;  // we should specify this globally, but we don't feel like it. "this is a hack."
+            jList1.setPreferredSize(new Dimension(300, originalWindowHeight));
+        }
+    }
+}
diff --git a/retina/chat/client/MessageManager.java b/retina/chat/client/MessageManager.java
new file mode 100644
index 0000000..2303e3d
--- /dev/null
+++ b/retina/chat/client/MessageManager.java
@@ -0,0 +1,182 @@
+
+package retina.chat.client;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import retina.chat.common.Packet;
+
+
+public class MessageManager extends Thread
+{
+    private ChatClient parent;
+    private String host;
+    private int port;
+    private Socket socket;
+
+    /** Creates a new instance of MessageManager */
+    public MessageManager(ChatClient parent, String host, int port)
+    {
+        this.parent = parent;
+        this.host   = host;
+        this.port   = port;
+    }
+
+    public void finalize()
+    {
+        disconnect();
+    }
+
+    public void run()
+    {
+        /**
+         * serialization code based on
+         * http://www.javapractices.com/Topic57.cjp
+         */
+        ObjectInput input = null;
+        try
+        {
+            while (true)
+            {
+                // deserialize the Packet
+                // InputStream buffer = new BufferedInputStream(socket.getInputStream());
+                input = new ObjectInputStream(socket.getInputStream());
+                Packet packet = (Packet)input.readObject();
+
+                if (packet == null)
+                {
+                    System.out.println("A null packet was read!!");
+                    continue;
+                }
+
+                if (parent == null)
+                {
+                    //System.out.println("null parent; received message" + packet.getMessageType());
+                    continue;
+                }
+
+                // do the magic with the packet
+                switch (packet.getMessageType())
+                {
+                    case Packet.MSGTYPE_SIGNON:
+                        //System.out.println("received a sign on message");
+                        parent.getAuthenticationManager().handleSignInMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_CREATE_NEW_GROUP:
+                        //System.out.println("received a create new group message");
+                        parent.getGroupManager().handleCreateAndJoinNewGroupMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_BROADCAST_NEW_GROUP:
+                        //System.out.println("received MSGTYPE_BROADCAST_NEW_GROUP");
+                        parent.getGroupManager().handleUpdateGroupListWithNewGroupMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_BROADCAST_REMOVE_GROUP:
+                        //System.out.println("received MSGTYPE_BROADCAST_REMOVE_GROUP");
+                        parent.getGroupManager().handleUpdateGroupListRemoveGroupMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_JOIN_GROUP:
+                        //System.out.println("received a join group message");
+                        parent.getGroupManager().handleJoinGroupMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_BROADCAST_NEW_GROUP_MEMBER:
+                        //System.out.println("received MSGTYPE_BROADCAST_NEW_GROUP_MEMBER");
+                        parent.getGroupManager().handleUpdateGroupMemberListWithNewMemberMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_BROADCAST_REMOVE_GROUP_MEMBER:
+                        //System.out.println("received MSGTYPE_BROADCAST_REMOVE_GROUP_MEMBER");
+                        parent.getGroupManager().handleUpdateGroupMemberListRemoveMemberMessage(packet);
+                        break;
+                    case Packet.MSGTYPE_BROADCAST_CHAT_MESSAGE:
+                        //System.out.println("received MSGTYPE_BROADCAST_CHAT_MESSAGE");
+                        parent.getChatManager().handleReceiveChatMessageMessage(packet);
+                        break;
+                    default:
+                        //System.out.println("received some other type of packet");
+                        break;
+                }
+            }
+        }
+        catch (java.net.SocketException e)
+        {
+            System.out.println("connection to server closed.");
+            System.exit(0);
+        }
+        catch (java.io.EOFException e)
+        {
+            System.out.println("connection to server closed unexpectedly.  fo sheezy, i'm queazy.");
+            System.exit(0);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public boolean connect()
+    {
+        try
+        {
+            socket = new Socket(host, port);
+            if (socket == null)
+            {
+                System.out.println("Could not create a socket to " + host + ":" + port);
+                return false;
+            }
+            return true;
+        }
+        catch (Exception e)
+        {
+            System.out.println("Could not create a socket to " + host + ":" + port + "; exception " + e.toString());
+            return false;
+        }
+    }
+
+    public void disconnect()
+    {
+        try
+        {
+            socket.close();
+        }
+        catch (Exception e)
+        {
+            System.out.println("MessageManager disconnect failed. " + e.toString());
+        }
+    }
+
+    /**
+     * returns true if packet was sent, or false on exception.
+     */
+    public static boolean sendMessage(Socket socket, Packet packet)
+    {
+        ObjectOutput output = null;
+        try
+        {
+            /**
+             * serialization code based on
+             * http://www.javapractices.com/Topic57.cjp
+             */
+            //OutputStream buffer = new BufferedOutputStream(socket.getOutputStream());
+            output = new ObjectOutputStream(socket.getOutputStream());
+            //output = new ObjectOutputStream(buffer);
+            output.writeObject(packet);
+            return true;
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+
+    public boolean sendMessage(Packet packet)
+    {
+        return sendMessage(socket, packet);
+    }
+}
diff --git a/retina/chat/client/NotMemberOfGroupException.java b/retina/chat/client/NotMemberOfGroupException.java
new file mode 100644
index 0000000..f3002ce
--- /dev/null
+++ b/retina/chat/client/NotMemberOfGroupException.java
@@ -0,0 +1,11 @@
+package retina.chat.client;
+
+public class NotMemberOfGroupException extends Exception
+{
+
+    /** Creates a new instance of NotMemberOfGroupException */
+    public NotMemberOfGroupException()
+    {
+    }
+
+}
diff --git a/retina/chat/client/Preferences.java b/retina/chat/client/Preferences.java
new file mode 100644
index 0000000..4685e25
--- /dev/null
+++ b/retina/chat/client/Preferences.java
@@ -0,0 +1,50 @@
+package retina.chat.client;
+
+
+
+public class Preferences
+{
+    public static final String SMALL = "Small";
+    public static final String MEDIUM = "Medium";
+    public static final String LARGE = "Large";
+    public static final String EXTRA_LARGE = "Extra Large";
+
+    public static final String BLACK = "Black";
+    public static final String BLUE = "Blue";
+    public static final String GREEN = "Green";
+    public static final String RED = "Red";
+
+    private String color;
+    private String size;
+
+    /** Creates a new instance of Preferences */
+    public Preferences()
+    {
+    }
+
+    public Preferences(String newColor, String newSize)
+    {
+        color = newColor;
+        size = newSize;
+    }
+
+    public void setColor(String newColor)
+    {
+        color = newColor;
+    }
+
+    public String getColor()
+    {
+        return color;
+    }
+
+    public void setSize(String newSize)
+    {
+        size = newSize;
+    }
+
+    public String getSize()
+    {
+        return size;
+    }
+}
diff --git a/retina/chat/client/PreferencesWindow.java b/retina/chat/client/PreferencesWindow.java
new file mode 100644
index 0000000..6aea397
--- /dev/null
+++ b/retina/chat/client/PreferencesWindow.java
@@ -0,0 +1,210 @@
+package retina.chat.client;
+
+/*
+ * PreferencesWindow.java
+ *
+ * Created on April 2, 2005, 5:20 PM
+ *
+ * To do list:
+ * modify ChatWindow constructor to create PreferencesWindow and Preferences member variables
+     with default values for Preferences
+ * add getPreferences method to ChatWindow
+ * modify ChatWindow to include color and size in the font for displayed text
+     in appendChatMessageToChatPanel:
+        String size = "4";
+        if (preferences.getSize().equals(Preferences.SMALL)) size = "-1";
+        else if (preferences.getSize().equals(Preferences.LARGE)) size = "+1";
+        else if (preferences.getSize().equals(Preferences.EXTRA_LARGE)) size = "+2";
+        appendTextToChatPanel("<font size=\"" + size + "\" color=" + preferences.getColor() + "><b>" + username + ":</b> " + message + "</font>");
+ * add a button (WHERE??) to show the PreferencesWindow
+ */
+
+
+public class PreferencesWindow extends javax.swing.JFrame
+{
+    ChatWindow chatWindow;
+
+    /** Creates new form PreferencesWindow */
+    public PreferencesWindow(ChatWindow chatWindow)
+    {
+        this.chatWindow = chatWindow;
+        initComponents();
+        initialize();
+        setLocationRelativeTo(chatWindow);
+        setTitle("Lederhosen 2.0: " + chatWindow.getGroupname() + " Preferences");
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jPanel1 = new javax.swing.JPanel();
+        colorLabel = new javax.swing.JLabel();
+        colorBox = new javax.swing.JComboBox();
+        sizeLabel = new javax.swing.JLabel();
+        sizeBox = new javax.swing.JComboBox();
+        okButton = new javax.swing.JButton();
+        cancelButton = new javax.swing.JButton();
+        jLabel1 = new javax.swing.JLabel();
+
+        setTitle("Lederhosen 2.0 Chat Window Preferences");
+        setResizable(false);
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent evt)
+            {
+                exitForm(evt);
+            }
+        });
+
+        jPanel1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 20, 20));
+
+        jPanel1.setMaximumSize(new java.awt.Dimension(350, 100));
+        jPanel1.setMinimumSize(new java.awt.Dimension(350, 100));
+        jPanel1.setPreferredSize(new java.awt.Dimension(350, 100));
+        colorLabel.setText("Color:");
+        jPanel1.add(colorLabel);
+
+        colorBox.setMaximumSize(new java.awt.Dimension(80, 19));
+        colorBox.setMinimumSize(new java.awt.Dimension(80, 19));
+        colorBox.setPreferredSize(new java.awt.Dimension(80, 19));
+        colorBox.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                colorBoxHandler(evt);
+            }
+        });
+
+        jPanel1.add(colorBox);
+
+        sizeLabel.setText("Font Size:");
+        jPanel1.add(sizeLabel);
+
+        sizeBox.setMaximumSize(new java.awt.Dimension(80, 19));
+        sizeBox.setMinimumSize(new java.awt.Dimension(80, 19));
+        sizeBox.setPreferredSize(new java.awt.Dimension(80, 19));
+        sizeBox.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                sizeBoxHandler(evt);
+            }
+        });
+
+        jPanel1.add(sizeBox);
+
+        okButton.setText("OK");
+        okButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                handleOkButton(evt);
+            }
+        });
+
+        jPanel1.add(okButton);
+
+        cancelButton.setText("Cancel");
+        cancelButton.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                handleCancelButton(evt);
+            }
+        });
+
+        jPanel1.add(cancelButton);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
+
+        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+        jLabel1.setText("Please select a color and font size.");
+        jLabel1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        getContentPane().add(jLabel1, java.awt.BorderLayout.NORTH);
+
+        pack();
+    }//GEN-END:initComponents
+
+    private void handleCancelButton(java.awt.event.ActionEvent evt)//GEN-FIRST:event_handleCancelButton
+    {//GEN-HEADEREND:event_handleCancelButton
+        setVisible(false);
+    }//GEN-LAST:event_handleCancelButton
+
+    private void handleOkButton(java.awt.event.ActionEvent evt)//GEN-FIRST:event_handleOkButton
+    {//GEN-HEADEREND:event_handleOkButton
+        chatWindow.getPreferences().setColor((String)colorBox.getSelectedItem());
+        chatWindow.getPreferences().setSize((String)sizeBox.getSelectedItem());
+        setVisible(false);
+    }//GEN-LAST:event_handleOkButton
+
+    private void sizeBoxHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_sizeBoxHandler
+    {//GEN-HEADEREND:event_sizeBoxHandler
+    }//GEN-LAST:event_sizeBoxHandler
+
+    private void colorBoxHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_colorBoxHandler
+    {//GEN-HEADEREND:event_colorBoxHandler
+    }//GEN-LAST:event_colorBoxHandler
+
+    /** Exit the Application */
+    private void exitForm(java.awt.event.WindowEvent evt)//GEN-FIRST:event_exitForm
+    {
+        setVisible(false);
+    }//GEN-LAST:event_exitForm
+
+    /**
+     * @param args the command line arguments
+
+    public static void main(String args[])
+    {
+        new PreferencesWindow().show();
+        //new PreferencesWindow().setVisible(true);
+    }
+     **/
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton cancelButton;
+    private javax.swing.JComboBox colorBox;
+    private javax.swing.JLabel colorLabel;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JButton okButton;
+    private javax.swing.JComboBox sizeBox;
+    private javax.swing.JLabel sizeLabel;
+    // End of variables declaration//GEN-END:variables
+
+    private void initialize()
+    {
+        colorBox.addItem(Preferences.BLACK);
+        colorBox.addItem(Preferences.BLUE);
+        colorBox.addItem(Preferences.GREEN);
+        colorBox.addItem(Preferences.RED);
+
+        sizeBox.addItem(Preferences.SMALL);
+        sizeBox.addItem(Preferences.MEDIUM);
+        sizeBox.addItem(Preferences.LARGE);
+        sizeBox.addItem(Preferences.EXTRA_LARGE);
+    }
+
+    public void show()
+    {
+        colorBox.setSelectedItem(chatWindow.getPreferences().getColor());
+        sizeBox.setSelectedItem(chatWindow.getPreferences().getSize());
+        super.show();
+    }
+
+    public void setVisible(boolean shouldSetVisible)
+    {
+        if (shouldSetVisible)
+        {
+            colorBox.setSelectedItem(chatWindow.getPreferences().getColor());
+            sizeBox.setSelectedItem(chatWindow.getPreferences().getSize());
+        }
+        super.setVisible(shouldSetVisible);
+    }
+
+
+}
diff --git a/retina/chat/client/ScrollTest.java b/retina/chat/client/ScrollTest.java
new file mode 100644
index 0000000..a9f0258
--- /dev/null
+++ b/retina/chat/client/ScrollTest.java
@@ -0,0 +1,54 @@
+
+package retina.chat.client;
+
+public class ScrollTest extends javax.swing.JFrame
+{
+
+    /** Creates new form ScrollTest */
+    public ScrollTest()
+    {
+        initComponents();
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jEditorPane1 = new javax.swing.JEditorPane();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
+        jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 200));
+        jEditorPane1.setEditable(false);
+        jEditorPane1.setText("\new\nds\nds\nds\nsd\n\n\n\n\nds\nfds\n\n\n\n\n\n\n\ndssf\nasd\nf\nasd\nf\nads\nfda\nsf\na\nsf\n");
+        jEditorPane1.setMinimumSize(new java.awt.Dimension(200, 200));
+        jScrollPane1.setViewportView(jEditorPane1);
+
+        getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
+
+        pack();
+    }//GEN-END:initComponents
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+            public void run()
+            {
+                new ScrollTest().setVisible(true);
+            }
+        });
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JEditorPane jEditorPane1;
+    private javax.swing.JScrollPane jScrollPane1;
+    // End of variables declaration//GEN-END:variables
+
+}
diff --git a/retina/chat/client/ScrollWindow.java b/retina/chat/client/ScrollWindow.java
new file mode 100644
index 0000000..85bfaf7
--- /dev/null
+++ b/retina/chat/client/ScrollWindow.java
@@ -0,0 +1,126 @@
+/*
+ * ScrollWindow.java
+ *
+ * Created on February 25, 2005, 9:02 PM
+ */
+package retina.chat.client;
+/**
+ *
+ * @author  chris
+ */
+public class ScrollWindow extends javax.swing.JFrame
+{
+    String text;
+
+    /** Creates new form ScrollWindow */
+    public ScrollWindow()
+    {
+        initComponents();
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jSplitPane1 = new javax.swing.JSplitPane();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        jEditorPane1 = new javax.swing.JEditorPane();
+        jTextArea1 = new javax.swing.JTextArea();
+        jPanel1 = new javax.swing.JPanel();
+        jButton1 = new javax.swing.JButton();
+        jTextField1 = new javax.swing.JTextField();
+
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent evt)
+            {
+                exitForm(evt);
+            }
+        });
+
+        jEditorPane1.setEditable(false);
+        jEditorPane1.setContentType("text/html");
+        jEditorPane1.setPreferredSize(new java.awt.Dimension(300, 200));
+        jScrollPane1.setViewportView(jEditorPane1);
+
+        jSplitPane1.setLeftComponent(jScrollPane1);
+
+        jSplitPane1.setRightComponent(jTextArea1);
+
+        getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);
+
+        jPanel1.setLayout(new java.awt.BorderLayout());
+
+        jButton1.setText("jButton1");
+        jButton1.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                buttonHandler(evt);
+            }
+        });
+
+        jPanel1.add(jButton1, java.awt.BorderLayout.EAST);
+
+        jTextField1.setText("jTextField1");
+        jTextField1.addMouseListener(new java.awt.event.MouseAdapter()
+        {
+            public void mouseClicked(java.awt.event.MouseEvent evt)
+            {
+                mouseClickedHandler(evt);
+            }
+        });
+
+        jPanel1.add(jTextField1, java.awt.BorderLayout.CENTER);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.SOUTH);
+
+        pack();
+    }//GEN-END:initComponents
+
+    boolean first = true;
+    private void mouseClickedHandler(java.awt.event.MouseEvent evt)//GEN-FIRST:event_mouseClickedHandler
+    {//GEN-HEADEREND:event_mouseClickedHandler
+        if (first)
+        {
+            jTextField1.setText("");
+            first = false;
+        }
+    }//GEN-LAST:event_mouseClickedHandler
+
+    boolean flag = false;
+    private void buttonHandler(java.awt.event.ActionEvent evt)//GEN-FIRST:event_buttonHandler
+    {//GEN-HEADEREND:event_buttonHandler
+        text += jTextField1.getText() + "<p>";
+        jEditorPane1.setText(text);
+        jTextField1.setText("");
+    }//GEN-LAST:event_buttonHandler
+
+    /** Exit the Application */
+    private void exitForm(java.awt.event.WindowEvent evt)//GEN-FIRST:event_exitForm
+    {
+        System.exit(0);
+    }//GEN-LAST:event_exitForm
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        new ScrollWindow().show();
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton jButton1;
+    private javax.swing.JEditorPane jEditorPane1;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JScrollPane jScrollPane1;
+    private javax.swing.JSplitPane jSplitPane1;
+    private javax.swing.JTextArea jTextArea1;
+    private javax.swing.JTextField jTextField1;
+    // End of variables declaration//GEN-END:variables
+
+}
diff --git a/retina/chat/client/TestServer.java b/retina/chat/client/TestServer.java
new file mode 100644
index 0000000..fe76304
--- /dev/null
+++ b/retina/chat/client/TestServer.java
@@ -0,0 +1,87 @@
+package retina.chat.client;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Vector;
+import retina.chat.common.Packet;
+
+public class TestServer extends Thread
+{
+    private int port;
+
+    private ServerSocket server = null;
+    private Socket socket = null;
+
+    /** Creates a new instance of TestServer */
+    public TestServer(int port)
+    {
+        this.port = port;
+    }
+
+    public static void main(String args[])
+    {
+        TestServer server = new TestServer(1234);
+        server.start();
+
+    }
+
+    public void run()
+    {
+        try
+        {
+            server = new ServerSocket(1234);
+            System.out.println("server waiting for connection");
+            socket = server.accept();
+            System.out.println("server got a connection!");
+
+            while(true)
+            {
+                handleMessage();
+            }
+
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        finally
+        {
+            try { server.close(); } catch (Exception e) { }
+        }
+
+    }
+
+    private void handleMessage() throws Exception
+    {
+        ObjectInput input = null;
+        input = new ObjectInputStream(socket.getInputStream());
+
+
+        Packet incomingPacket = (Packet)input.readObject();
+
+        int type = incomingPacket.getMessageType();
+        type = type == Packet.MSGTYPE_CHAT_MESSAGE ? Packet.MSGTYPE_BROADCAST_CHAT_MESSAGE : type;
+        String username = incomingPacket.getUsername();
+        String chatMessage = incomingPacket.getChatMessage();
+        String groupname = incomingPacket.getGroupname();
+        System.out.println("message type = " + type);
+
+        Packet outgoingPacket = new Packet(type);
+        outgoingPacket.setStatusCode(Packet.SUCCESS);
+        outgoingPacket.setUsername(username);
+        outgoingPacket.setChatMessage(chatMessage);
+        outgoingPacket.setGroupname(groupname);
+        Vector list = new Vector();
+        list.add("Chris");
+        list.add("Howie");
+        outgoingPacket.setDataVector(list);
+
+        MessageManager.sendMessage(socket, outgoingPacket);
+
+    }
+
+}
diff --git a/retina/chat/common/Packet.java b/retina/chat/common/Packet.java
new file mode 100644
index 0000000..a2031ea
--- /dev/null
+++ b/retina/chat/common/Packet.java
@@ -0,0 +1,106 @@
+
+package retina.chat.common;
+
+import java.io.Serializable;
+import java.util.Vector;
+
+public class Packet implements Serializable
+{
+    // message types
+    public static final int MSGTYPE_SIGNON     = 1;
+    public static final int MSGTYPE_CREATE_NEW_GROUP = 2;
+    public static final int MSGTYPE_BROADCAST_NEW_GROUP = 3;
+    public static final int MSGTYPE_BROADCAST_REMOVE_GROUP = 4;
+    public static final int MSGTYPE_JOIN_GROUP = 5;
+    public static final int MSGTYPE_BROADCAST_NEW_GROUP_MEMBER = 6;
+    public static final int MSGTYPE_BROADCAST_REMOVE_GROUP_MEMBER = 7;
+    public static final int MSGTYPE_CHAT_MESSAGE = 8;
+    public static final int MSGTYPE_BROADCAST_CHAT_MESSAGE = 9;
+    public static final int MSGTYPE_LEAVE_GROUP = 10;
+    public static final int MSGTYPE_SIGN_OUT = 11;
+    public static final int MSGTYPE_HELP = 12;
+
+    // generic error codes
+    public static final int SUCCESS            = 100;
+
+    // error codes for SIGNON messages
+    public static final int ERROR_USER_EXISTS  = 101;
+    public static final int ERROR_USER_EMPTY  = 105;
+
+    // error codes for CREATE_NEW_GROUP messages
+    public static final int ERROR_GROUP_EXISTS = 102;
+    public static final int ERROR_GROUP_EMPTY = 106;
+
+    // error codes for JOIN_GROUP messages
+    public static final int ERROR_ALREADY_IN_GROUP = 103;
+    public static final int ERROR_GROUP_DOES_NOT_EXIST = 104;
+
+    private int messageType = -1;
+    private int statusCode  = -1;
+    private String chatMessage = null;
+    private Vector dataVector = null;
+    private String groupname = null;
+    private String username = null;
+
+
+    /** Creates a new instance of Packet. we'll set the values later with accessors. */
+    public Packet(int messageType)
+    {
+        this.messageType = messageType;
+    }
+
+    public int getMessageType()
+    {
+        return messageType;
+    }
+
+    public void setStatusCode(int statusCode)
+    {
+        this.statusCode = statusCode;
+    }
+
+    public int getStatusCode()
+    {
+        return statusCode;
+    }
+
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setDataVector(Vector data)
+    {
+        this.dataVector = data;
+    }
+
+    public Vector getDataVector()
+    {
+        return dataVector;
+    }
+
+    public void setGroupname(String groupname)
+    {
+        this.groupname = groupname;
+    }
+
+    public String getGroupname()
+    {
+        return groupname;
+    }
+
+    public void setChatMessage(String chatMessage)
+    {
+        this.chatMessage = chatMessage;
+    }
+
+    public String getChatMessage()
+    {
+        return chatMessage;
+    }
+}
diff --git a/retina/chat/server/ChatServer.java b/retina/chat/server/ChatServer.java
new file mode 100644
index 0000000..1d1b4cb
--- /dev/null
+++ b/retina/chat/server/ChatServer.java
@@ -0,0 +1,266 @@
+package retina.chat.server;
+
+import java.util.*;
+import java.net.*;
+import java.io.*;
+
+
+public class ChatServer {
+	public static final String LOG_LOCATION = "./";
+
+    public static final int DEFAULT_PORT =4444;
+    public static final String DEFAULT_GROUP = "RETINA HELP";
+    private int serverPortNumber;
+
+    Vector users;
+    Vector groups;
+    Vector sockets;
+    Vector messQ;
+    HashMap user2Socket;
+    HashMap socket2User;
+    Handler handler;
+    MessageWriter writer;
+
+    File log;
+    FileWriter out;
+
+    /** Creates a new instance of LederhosenServer */
+    public ChatServer(int serverPortNumber) {
+        users = new Vector();
+        groups = new Vector();
+        sockets = new Vector();
+        messQ = new Vector();
+        user2Socket = new HashMap();
+        socket2User = new HashMap();
+        handler = new Handler(this);
+        writer = new MessageWriter(this);
+        this.serverPortNumber = serverPortNumber;
+
+        log = new File(LOG_LOCATION + System.currentTimeMillis() + "_SYSTEM.log");
+
+	try {
+	    log.createNewFile();
+	    out = new FileWriter(log, true);
+
+	} catch (FileNotFoundException e){
+	    System.out.println("Could not open log file for server");
+			System.out.println(e.getMessage());
+			out = null;
+	} catch (Exception e) {
+	    out = null;
+	}
+
+	// default group
+	groups.add(new Group(DEFAULT_GROUP, null));
+    }
+
+    /**
+     *  RUNNIT
+     */
+    public static void main(String[] args){
+        String usageStatement = "Usage: java retina.chat.server.ChatServer SERVER_PORT";
+
+        // set port number to default port.  only change it if command-line argument port number read correctly.
+        int serverPortNumber = DEFAULT_PORT;
+        if (args.length == 1)
+        {
+            try
+            {
+                serverPortNumber = Integer.parseInt(args[0]);
+            }
+            catch(NumberFormatException e)
+            {
+                System.out.println("Error reading specified port number. Trying to listen on the default port (" + DEFAULT_PORT + ").");
+            }
+        }
+
+        ChatServer L = new ChatServer(serverPortNumber);
+        L.run();
+    }
+
+    /**
+     *  Function:run
+     *  Desc: Begin Processing Messages
+     */
+    public void run(){
+        //set up the server socket
+        ServerSocket server = null;
+
+        try{
+            server = new ServerSocket(serverPortNumber);
+
+			String logStart = handler.getTimestamp() + "Retina ChatServer started!";
+			System.out.println(logStart);
+			writeLog(logStart);
+        }
+        catch(BindException be)
+        {
+            System.out.println("****ERROR: The port number " + serverPortNumber + " is already in use!");
+            System.exit(1);
+        }
+        catch(IOException e){
+            System.out.println("****ERROR: An IOException occurred: " + e.toString());
+            System.exit(1);
+        }
+        catch(Exception e){
+            System.out.println("*****Error happened in Server");
+            e.printStackTrace();
+            System.exit(1);
+        }
+
+
+//        System.out.println("Starting Server Socket");
+        // set up the socket request listener
+        ServerSocketListenerThread sslt = new ServerSocketListenerThread(this, server);
+        sslt.start();
+
+		BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
+
+        // start processing messages
+        try{
+			while(!input.ready() || !input.readLine().equals("quit")){
+				Message msg = popQ();
+				if (msg != null){
+				  //  System.out.println("have a message");
+					handler.process(msg);
+				}
+			}
+		} catch (Exception e) {
+			System.err.println(e.getMessage());
+		}
+
+		String logMessage = handler.getTimestamp() + "ServerShutdown";
+		System.out.println(logMessage);
+		writeLog(logMessage);
+
+		System.exit(0);
+    }
+
+    /**** new users functions ****/
+
+    public void addSocket(Socket s){
+        if(s != null)
+            sockets.add(s);
+    }
+
+    public void addUser(User u, Socket s){
+        if(u != null && s != null){
+            users.add(u);
+            user2Socket.put(u, s);
+            socket2User.put(s, u);
+        }
+    }
+
+    public void addGroup2User(User u, Group g){
+        if(u != null && g != null){
+            int i = users.indexOf(u);
+            ((User) users.elementAt(i)).addGroup(g);
+         //   System.out.println("Added group to the user");
+        }
+    }
+
+    public void addUser2Group(User u, Group g){
+         if(u != null && g != null){
+            int i = groups.indexOf(g);
+            ((Group) groups.elementAt(i)).addUser(u);
+        //    System.out.println("Added user to the group");
+        }
+    }
+
+    public void removeUserFromGroup(User u, Group g){
+       if(u != null && g != null){
+            int i = users.indexOf(u);
+            int j = groups.indexOf(g);
+
+            if(i >= 0 && j >= 0){
+//                ((User) users.elementAt(i)).removeGroup(g);
+                ((Group) groups.elementAt(j)).removeUser(u);
+
+                if(g.users.size() == 0 && g.getName().equals(DEFAULT_GROUP) == false) {
+                    g.closeOutput();
+                    groups.removeElementAt(j);
+                }
+            }
+         }
+    }
+
+    public void removeUser(Socket s){
+        if(s == null) return;
+
+        User u = (User)socket2User.get(s);
+
+        if(u == null) return;
+
+        // remove user from groups
+        for(int i = 0; i < u.groups.size(); i++){
+            Group g = (Group) u.groups.elementAt(i);
+            if(g != null)
+                removeUserFromGroup(u, g);
+        }
+
+        users.removeElement(u);
+        user2Socket.remove(u);
+        socket2User.remove(s);
+        sockets.remove(s);
+    }
+
+    /**** group functions ****/
+    public void addGroup(Group g){
+        if(g != null)
+            groups.addElement(g);
+    }
+
+    public Group findGroup(String gName){
+        if(gName == null || gName.equals("")) return null;
+        for(int i = 0; i < groups.size(); i++){
+            if(((Group)groups.elementAt(i)).getName().equals(gName)){
+                return (Group) groups.elementAt(i);
+            }
+        }
+        return null;
+    }
+
+    public Vector getSockets(Group g){
+        if(g == null) return null;
+
+        Vector socks = new Vector();
+        for(int i = 0; i < g.users.size(); i++){
+            User u  = (User) g.users.elementAt(i);
+            socks.addElement((Socket)user2Socket.get(u));
+        }
+        return socks;
+    }
+
+    /***** Message Q functions *****/
+
+    public void queue(Message m){
+        if(m != null)
+            messQ.addElement(m);
+    }
+
+    public Message popQ(){
+        if(messQ.isEmpty()) return null;
+        else return (Message)messQ.remove(0);
+    }
+
+	/** Log methods */
+
+    public void writeLog(String s){
+        if (out != null){
+            try{
+                out.write(s + "\n");
+                out.flush();
+            } catch (Exception e){
+            }
+        }
+    }
+
+    public void closeOutput(){
+        if (out != null){
+            try{
+                out.close();
+            } catch (Exception e) {
+            }
+        }
+    }
+}
diff --git a/retina/chat/server/Group.java b/retina/chat/server/Group.java
new file mode 100644
index 0000000..cc0ba13
--- /dev/null
+++ b/retina/chat/server/Group.java
@@ -0,0 +1,96 @@
+package retina.chat.server;
+
+import java.util.*;
+import java.io.*;
+
+public class Group {
+    String groupName;
+    Vector users;
+    File log;
+    FileWriter out;
+
+    /** Creates a new instance of User */
+    public Group(String name, User firstUser) {
+        groupName = name;
+        users = new Vector();
+	if (firstUser != null)
+	{
+	    users.addElement(firstUser);
+	    firstUser.addGroup(this);
+	}
+
+        log = new File(ChatServer.LOG_LOCATION + System.currentTimeMillis() + "_" +  groupName + ".log");
+        try {
+            log.createNewFile();
+            out = new FileWriter(log, true);
+
+        } catch (FileNotFoundException e){
+            System.out.println("Could not open log file for group: " + groupName);
+            System.out.println(e.getMessage());
+            out = null;
+        } catch (Exception e) {
+            out = null;
+        }
+
+    }
+
+    /**
+     *  Function: getName
+     *  Desc: returns the usr name
+     */
+    public String getName(){
+        return groupName;
+
+    }
+
+    /**
+     *  Function: inGroup
+     *  Desc: returns if the user is in a group
+     */
+
+
+    public boolean isInGroup(User u){
+        if(u != null)
+            return users.contains(u);
+
+        return false;
+    }
+
+    /**
+     *  Function: addGroup
+     *  Desc: adds a group to the user group vector
+     */
+    public void addUser(User u){
+        if(u != null && !users.contains(u))
+            users.addElement(u);
+    }
+
+    /**
+     *  Function: removeGroup
+     *  Desc: remove a user from a group
+     */
+    public void removeUser(User u){
+        if(u != null && users.contains(u)){
+            users.removeElement(u);
+        }
+    }
+
+    public void writeLog(String s){
+        if (out != null){
+            try{
+                out.write(s + "\n");
+                out.flush();
+            } catch (Exception e){
+            }
+        }
+    }
+
+    public void closeOutput(){
+        if (out != null){
+            try{
+                out.close();
+            } catch (Exception e) {
+            }
+        }
+    }
+}
diff --git a/retina/chat/server/Handler.java b/retina/chat/server/Handler.java
new file mode 100644
index 0000000..718fe38
--- /dev/null
+++ b/retina/chat/server/Handler.java
@@ -0,0 +1,432 @@
+package retina.chat.server;
+
+import retina.chat.common.Packet;
+import java.util.*;
+import java.net.*;
+import java.text.*;
+
+
+public class Handler {
+
+    private ChatServer server;
+
+    /** Creates a new instance of Handler */
+    public Handler(ChatServer server) {
+        this.server = server;
+    }
+
+    /** The main access to the Handler class.  This method is called with every
+	 *  message that is passed to the server
+	 */
+	public boolean process(Message message){
+        if (message == null) return false;
+
+		Packet packet = message.getPacket();
+
+		if (packet == null) return false;
+
+        switch (packet.getMessageType())	{
+			case Packet.MSGTYPE_SIGNON:
+				//System.out.println("received a sign on message");
+				//parent.getAuthenticationManager().handleSignInMessage(packet);
+				signOnServerHandler(message);
+				break;
+			case Packet.MSGTYPE_CREATE_NEW_GROUP:
+				//System.out.println("received a create new group message");
+				//  parent.getGroupManager().handleCreateAndJoinNewGroupMessage(packet);
+				createGroupHandler(message);
+				break;
+			case Packet.MSGTYPE_JOIN_GROUP:
+				//System.out.println("received a join group message");
+			    //parent.getGroupManager().handleJoinGroupMessage(packet);
+				joinGroupHandler(message);
+				break;
+			case Packet.MSGTYPE_LEAVE_GROUP:
+				//System.out.println("received a join group message");
+			    //parent.getGroupManager().handleJoinGroupMessage(packet);
+				leaveGroupHandler(message);
+				break;
+			case Packet.MSGTYPE_SIGN_OUT:
+				//System.out.println("received a join group message");
+			    //parent.getGroupManager().handleJoinGroupMessage(packet);
+				signOutServerHandler(message);
+				break;
+			case Packet.MSGTYPE_CHAT_MESSAGE:
+				//System.out.println("received a join group message");
+			    //parent.getGroupManager().handleJoinGroupMessage(packet);
+			    	if (message.getPacket().getGroupname().equals(server.DEFAULT_GROUP))
+				    specialMessageHandler(message);
+				else
+				    // the default... it's just a regular old chat message
+				    chatMessageHandler(message);
+				break;
+			default:
+				System.out.println("received some unknown type of packet");
+				System.out.println("Message type: " + packet.getMessageType());
+				break;
+		}
+        return true;
+		//debugUser();
+		//debugGroups();
+    }
+
+    /**
+	 * Processes a message of type Packet.MSGTYPE_SIGNON
+	 */
+	public boolean signOnServerHandler(Message message){
+        String userName = message.getPacket().getUsername();
+		//System.out.println("username: " + userName);
+
+		Socket socket = message.getSocket();
+
+        if (userName == null || userName.equals("")){
+            server.writer.signOnFailed(socket, Packet.ERROR_USER_EMPTY);
+            System.out.println(getTimestamp() + "SignOnFailed: Empty username");
+			return false;
+        }
+
+        for (int i = 0; i < server.users.size(); i++){
+            if (((User)server.users.get(i)).getName().equals(userName)){
+                server.writer.signOnFailed(socket, Packet.ERROR_USER_EXISTS);
+                System.out.println(getTimestamp() + "SignOnFailed: Username " + userName + " already in use");
+                return false;
+            }
+        }
+
+        // add the user to the user list and set up the hashmap.
+        User newUser = new User(userName);
+        server.addUser(newUser, socket);
+
+        // send OK back to user
+        server.writer.signOnOK(socket, server.groups);
+
+		String logMessage = getTimestamp() + "SignOnOK: " + userName;
+        System.out.println(logMessage);
+		server.writeLog(logMessage);
+        return true;
+    }
+
+
+    /**
+	 * Processes a message of type Packet.MSGTYPE_CREATE_NEW_GROUP
+	 */
+    public boolean createGroupHandler(Message message){
+        String groupName = message.getPacket().getGroupname();
+		//System.out.println("new Group: " + groupName);
+
+		Socket socket = message.getSocket();
+
+        if (groupName == null || groupName.equals("")){
+            server.writer.openGroupFailed(socket, Packet.ERROR_GROUP_EMPTY);
+            System.out.println(getTimestamp() + "OpenGroupFailed: Empty groupname");
+            return false;
+        }
+
+        Group group = server.findGroup(groupName);
+        if (group != null){
+          server.writer.openGroupFailed(socket, Packet.ERROR_GROUP_EXISTS);
+          System.out.println(getTimestamp() + "OpenGroupFailed: Groupname " + groupName + " already in use");
+          return false;
+        }
+
+        //add the group to the group list
+        User user = (User)server.socket2User.get(socket);
+        // System.out.println("username: " + u.getName());
+        Group newGroup = new Group(groupName, user);
+        server.addGroup(newGroup);
+        server.addGroup2User(user, newGroup);
+
+        // send back to the user
+        server.writer.openGroupOK(socket, groupName);
+        server.writer.broadcastNewGroup(server.sockets, groupName);
+
+        String logMessage = getTimestamp() + "OpenGroupOK: " + groupName;
+        System.out.println(logMessage);
+        newGroup.writeLog(logMessage);
+		server.writeLog(logMessage);
+        return true;
+    }
+
+    /**
+	 * Processes a message of type Packet.MSGTYPE_JOIN_GROUP
+	 */
+    public boolean joinGroupHandler(Message message){
+        String groupName = message.getPacket().getGroupname();
+		//System.out.println("groupName: " + groupName);
+
+		Socket socket = message.getSocket();
+
+        if (groupName == null || groupName.equals("")){
+            server.writer.joinGroupFailed(socket, Packet.ERROR_GROUP_EMPTY);
+            System.out.println(getTimestamp() + "JoinGroupFailed: Empty groupname");
+            return false;
+        }
+
+        // group does not exist
+        Group group = server.findGroup(groupName);
+        if (group == null){
+          server.writer.joinGroupFailed(socket, Packet.ERROR_GROUP_DOES_NOT_EXIST);
+          System.out.println(getTimestamp() + "JoinGroupFailed: Group " + groupName + " does not exist");
+          return false;
+        }
+
+        User user = (User)server.socket2User.get(socket);
+
+		if (user == null) return false;
+
+		String userName = user.getName();
+
+        //user already in group
+        if (group.isInGroup(user)){
+          server.writer.joinGroupFailed(socket, Packet.ERROR_ALREADY_IN_GROUP);
+          System.out.println(getTimestamp() + "JoinGroupFailed: User " + userName + " is already in group " + groupName);
+          return false;
+        }
+
+        //System.out.println("userName: " + u.getName());
+        server.addUser2Group(user, group);
+        server.addGroup2User(user, group);
+
+        //group = server.findGroup(groupName);
+
+        // send back to the user
+        server.writer.joinGroupOK(socket, group.users, user, groupName);
+        server.writer.broadcastNewJoin(server.getSockets(group), groupName,  userName);
+
+        String logMessage = getTimestamp() + "JoinGroupOK: " + groupName + ", " + userName;
+        System.out.println(logMessage);
+        group.writeLog(logMessage);
+        return true;
+    }
+
+    /**
+	 * Processes a message of type Packet.MSGTYPE_LEAVE_GROUP
+	 */
+    public boolean leaveGroupHandler(Message message){
+        String groupName = message.getPacket().getGroupname();
+		//System.out.println("Leaving Group: " + groupName);
+
+		Socket socket = message.getSocket();
+
+		//System.out.println("username: " + u.getName());
+        Group group = server.findGroup(groupName);
+
+        if (groupName == null || groupName.equals("") || group == null){
+            //server.writer.leaveGroupFailed(m.getSocket(), Packet.ERROR_GROUP_EMPTY);
+            System.out.println(getTimestamp() + "LeaveGroupFailed: Groupname empty");
+            return false;
+        }
+
+        User user = (User)server.socket2User.get(socket);
+
+        boolean shouldDestroyGroup = (group.users.size() == 1) && (groupName.equals(server.DEFAULT_GROUP) == false);
+
+        server.writer.leaveGroupOK(socket, groupName);
+        server.writer.broadcastLeaveGroup(server.getSockets(group), groupName, user.getName());
+
+        if (shouldDestroyGroup){
+            server.writer.broadcastRemoveGroup(server.sockets, groupName);
+        }
+
+        String logMessage = getTimestamp() + "LeaveGroupOK: " + groupName + ", " + user.getName();
+        System.out.println(logMessage);
+        group.writeLog(logMessage);
+
+        if (shouldDestroyGroup){
+			String closeMessage = getTimestamp() + "GroupClosed: " + groupName;
+			System.out.println(closeMessage);
+			server.writeLog(closeMessage);
+			group.writeLog(closeMessage);
+		}
+
+        server.removeUserFromGroup(user, group);
+
+        return true;
+
+    }
+
+    /**
+	 * Processes a message of type Packet.MSGTYPE_SIGN_OUT
+	 */
+    public boolean signOutServerHandler(Message message){
+		//String userName = message.getPacket().getUsername();
+		//System.out.println("Leaving program: " + userName);
+
+/*        if (userName == null || userName.equals("")){
+            System.out.println(getTimestamp() + "SignOutFailed: Username empty");
+            return false;
+        }
+*/
+		Socket socket = message.getSocket();
+
+        User user = (User)server.socket2User.get(socket);
+
+        if (user == null){
+            System.out.println(getTimestamp() + "SignOutFailed: User empty");
+            return false;
+        }
+
+        String userName = user.getName();
+
+        if (userName == null || userName.equals("")){
+            System.out.println(getTimestamp() + "SignOutFailed: Username empty");
+            return false;
+        }
+
+        // remove the user from each of its groups
+        for(int i = 0; i < user.groups.size(); i++){
+            Group group = (Group) user.groups.elementAt(i);
+
+            String logMessage = getTimestamp() + "LeaveGroupOK: " + group.getName() + ", " + userName;
+            group.writeLog(logMessage);
+            System.out.println(logMessage);
+
+            server.writer.leaveGroupOK(socket, group.getName());
+            server.writer.broadcastLeaveGroup(server.getSockets(group), group.getName(), userName);
+
+	    if (group.users.size() == 1 && group.getName().equals(server.DEFAULT_GROUP) == false) {
+				server.writer.broadcastRemoveGroup(server.sockets, group.getName());
+				String closeMessage = getTimestamp() + "GroupClosed: " + group.getName();
+				System.out.println(closeMessage);
+				server.writeLog(closeMessage);
+				group.writeLog(closeMessage);
+			}
+        }
+
+		//server.writer.signOffOK(socket);
+        server.removeUser(socket);
+
+		String logMessage = getTimestamp() + "SignOutServerOK: " + userName;
+
+        System.out.println(logMessage);
+		server.writeLog(logMessage);
+
+        return true;
+    }
+
+
+
+    /**
+     * This is for handling a chat message that is to be broadcast to the whole group.
+     */
+    private boolean chatMessageHandler(Message message){
+        String groupName = message.getPacket().getGroupname();
+        String chatMessage = message.getPacket().getChatMessage();
+	//System.out.println("Group: " + groupName);
+        //System.out.println("Chat Message: " + chatMessage);
+
+	//
+	Socket socket = message.getSocket();
+
+        if (groupName == null || groupName.equals("") || chatMessage == null || chatMessage.equals("")){
+            return false;
+        }
+
+        User user = (User)server.socket2User.get(socket);
+		//System.out.println("username: " + u.getName());
+        Group group = server.findGroup(groupName);
+
+        server.writer.broadcastChatMessage(server.getSockets(group), chatMessage, groupName, user.getName());
+
+        String logMessage = getTimestamp() + "ChatMessage: " + groupName + ", " + user.getName() + " :: " + chatMessage;
+        System.out.println(logMessage);
+        group.writeLog(logMessage);
+
+        return true;
+    }
+
+    /**
+     * This method is for handling any special request. The "message" parameter contains everything
+     * for sending back the response; the "msg" is the text that the user typed.
+     */
+    private boolean specialMessageHandler(Message message)
+    {
+	// don't change this part!!
+	Packet packet = new Packet(Packet.MSGTYPE_BROADCAST_CHAT_MESSAGE);
+	packet.setUsername("RETINA");
+	packet.setGroupname(server.DEFAULT_GROUP);
+
+	// get the message
+	String chatMessage = message.getPacket().getChatMessage();
+       	// strip out the HTML parts
+	String sub = chatMessage.substring(chatMessage.indexOf(">")+1, chatMessage.length());
+	String msg = sub.substring(0, sub.indexOf("<"));
+
+	String reply = null;
+	// this is the part that figures out what to send back
+	if (msg.equals("help"))
+	    reply = "Do you need help?";
+	else if (msg.equals("who"))
+	{
+	    reply = "Here are the people currently logged in<br>";
+	    for (Object user : server.users)
+		reply += user + "<br>";
+	}
+	else if (msg.startsWith("broadcast"))
+	{
+	    msg = msg.substring(10, msg.length());
+	    message.getPacket().setChatMessage(msg);
+	    return chatMessageHandler(message);
+	}
+	else reply = "I'm sorry, I don't understand " + msg;
+
+	// don't change this part!!
+	packet.setChatMessage(reply);
+	server.writer.writeOut(message.getSocket(), packet);
+
+	// TODO: add a log message
+
+	return true;
+    }
+
+
+
+    /**
+     * Prints out debugging info about users in the system
+     */
+    public void debugUser(){
+        System.out.println("System Users #" + server.users.size());
+        System.out.println("num sockets: " + server.sockets.size());
+        for(int i = 0; i < server.users.size(); i++){
+            User user = (User)server.users.elementAt(i);
+            System.out.println("user" + i + ": " + user.getName());
+        }
+        System.out.println("");
+    }
+
+
+    /**
+	 * Prints out debugging info about groups in the system
+	 */
+    public void debugGroups(){
+        System.out.println("System groups #" + server.groups.size());
+        for(int i = 0; i < server.groups.size(); i++){
+            Group group = (Group)server.groups.elementAt(i);
+            System.out.println("Group Name: " + group.getName());
+            for(int j = 0; j < group.users.size(); j++){
+                User user = (User)group.users.elementAt(j);
+                System.out.println("    user" + j + ": " + user.getName());
+            }
+            System.out.println("");
+        }
+    }
+
+    /**
+	 * Creates a time stamp string
+	 */
+    public String getTimestamp(){
+        Calendar time = Calendar.getInstance();
+
+        int year = time.get(Calendar.YEAR);
+        int month = time.get(Calendar.MONTH) + 1;
+        int day = time.get(Calendar.DAY_OF_MONTH);
+        int hour = time.get(Calendar.HOUR_OF_DAY);
+        int minute = time.get(Calendar.MINUTE);
+        int second = time.get(Calendar.SECOND);
+
+		NumberFormat formatter = NumberFormat.getInstance();
+		formatter.setMinimumIntegerDigits(2);
+
+        return (year + "/" + formatter.format((long)month) + "/" + formatter.format((long)day) + "  " + formatter.format((long)hour) + ":" + formatter.format((long)minute) + ":" + formatter.format((long)second) + "\t");
+    }
+}
diff --git a/retina/chat/server/Message.java b/retina/chat/server/Message.java
new file mode 100644
index 0000000..16ea14c
--- /dev/null
+++ b/retina/chat/server/Message.java
@@ -0,0 +1,27 @@
+package retina.chat.server;
+
+import retina.chat.common.Packet;
+
+import java.net.*;
+
+
+public class Message{
+    //String userName;
+    Packet packet;
+    Socket socket;
+
+    /** Creates a new instance of Message */
+    public Message(Socket s, Packet p) {
+      //  userName = "";
+        socket = s;
+        packet = p;
+    }
+
+    public Packet getPacket(){
+        return packet;
+    }
+
+    public Socket getSocket(){
+        return socket;
+    }
+}
diff --git a/retina/chat/server/MessageWriter.java b/retina/chat/server/MessageWriter.java
new file mode 100644
index 0000000..94eb66f
--- /dev/null
+++ b/retina/chat/server/MessageWriter.java
@@ -0,0 +1,216 @@
+package retina.chat.server;
+
+
+import retina.chat.common.Packet;
+
+import java.util.*;
+import java.net.*;
+import java.io.*;
+
+public class MessageWriter {
+    ChatServer server;
+
+    /** Creates a new instance of MessageWriter */
+    public MessageWriter(ChatServer L) {
+        server = L;
+    }
+
+    public void writeOut(Socket s, Packet p){
+        if(s == null || p == null) return;
+
+        if(!s.isConnected()) return;
+
+        ObjectOutput output = null;
+        try
+        {
+            output = new ObjectOutputStream(s.getOutputStream());
+            output.writeObject(p);
+        }
+        catch (Exception e)
+        {
+            //System.out.println(e.getMessage());
+           // e.printStackTrace();
+        }
+    }
+
+    /* sign on to the server messages */
+    // TESTED
+    public void signOnOK(Socket s, Vector groups){
+        if( s == null) return;
+
+//        System.out.println("sign On OK");
+    //    System.out.println("");
+
+        Packet p = new Packet(Packet.MSGTYPE_SIGNON);
+        p.setStatusCode(Packet.SUCCESS);
+
+        Vector groupNames = new Vector();
+        for(int i = 0; i < groups.size(); i++){
+            Group g = (Group)groups.elementAt(i);
+            groupNames.addElement(g.getName());
+        }
+
+        p.setDataVector(groupNames);
+
+        writeOut(s, p);
+    }
+
+    // TESTED
+    public void signOnFailed(Socket s, int error){
+        if(s == null) return;
+
+//        System.out.println("sign on failed");
+        Packet p = new Packet(Packet.MSGTYPE_SIGNON);
+        p.setStatusCode(error);
+        writeOut(s, p);
+    }
+
+    /* open group messages */
+    // TESTED
+    public void openGroupOK(Socket s, String groupName){
+        if(s == null || groupName == null || groupName.equals("")) return;
+//        System.out.println("create group ok");
+        Packet p = new Packet(Packet.MSGTYPE_CREATE_NEW_GROUP);
+        p.setStatusCode(Packet.SUCCESS);
+        p.setGroupname(groupName);
+        writeOut(s, p);
+    }
+
+    // TESTED
+    public void openGroupFailed(Socket s, int error){
+        if(s == null) return;
+
+//        System.out.println("create group failed");
+        Packet p = new Packet(Packet.MSGTYPE_CREATE_NEW_GROUP);
+        p.setStatusCode(error);
+        writeOut(s, p);
+    }
+    // TESTED
+    public void broadcastNewGroup(Vector sockets, String groupName){
+        if(sockets == null || groupName == null || groupName.equals("")) return;
+
+        Packet p = new Packet(Packet.MSGTYPE_BROADCAST_NEW_GROUP);
+        p.setGroupname(groupName);
+
+        for(int i = 0; i < sockets.size(); i++){
+            Socket s = (Socket)sockets.elementAt(i);
+            if(s != null)
+                writeOut(s, p);
+        }
+    }
+
+    /* join group messages */
+    // TESTED
+    public void joinGroupOK(Socket s, Vector users, User me, String groupName){
+        if(s == null || users == null || me == null || groupName == null || groupName.equals("")) return;
+
+//        System.out.println("join group ok");
+        Packet p = new Packet(Packet.MSGTYPE_JOIN_GROUP);
+        p.setStatusCode(Packet.SUCCESS);
+        p.setGroupname(groupName);
+
+        Vector userList = new Vector();
+        for(int i = 0; i < users.size(); i++){
+            User u = (User) users.elementAt(i);
+            if(!u.equals(me))
+                userList.addElement(u.getName());
+        }
+        p.setDataVector(userList);
+
+        writeOut(s, p);
+    }
+
+    // TESTED
+    public void joinGroupFailed(Socket s, int error_code){
+        if(s == null) return;
+
+//        System.out.println("join group failed");
+        Packet p = new Packet(Packet.MSGTYPE_JOIN_GROUP);
+        p.setStatusCode(error_code);
+        writeOut(s, p);
+    }
+
+    public void broadcastNewJoin(Vector sockets, String groupName, String userName){
+        if(sockets == null || groupName == null || groupName.equals("") || userName == null || userName.equals("")) return;
+
+        Packet p = new Packet(Packet.MSGTYPE_BROADCAST_NEW_GROUP_MEMBER);
+        p.setUsername(userName);
+        p.setGroupname(groupName);
+
+        for(int i = 0; i < sockets.size(); i++){
+            Socket s = (Socket)sockets.elementAt(i);
+            if(s != null)
+                writeOut(s, p);
+        }
+    }
+
+    /* leave group messages */
+    // TESTED
+    public void leaveGroupOK(Socket s, String groupName){
+        if(s == null || groupName == null || groupName.equals("")) return;
+
+//        System.out.println("Leaving Group OK");
+        Packet p = new Packet(Packet.MSGTYPE_LEAVE_GROUP);
+        p.setStatusCode(Packet.SUCCESS);
+        p.setGroupname(groupName);
+
+        writeOut(s, p);
+    }
+
+    // TESTED
+    public void broadcastLeaveGroup(Vector sockets, String groupName, String userName){
+        if(sockets == null || groupName == null || groupName.equals("") || userName == null || userName.equals("")) return;
+
+//        System.out.println("broadcast leaving group");
+        Packet p = new Packet(Packet.MSGTYPE_BROADCAST_REMOVE_GROUP_MEMBER);
+        p.setGroupname(groupName);
+        p.setUsername(userName);
+
+         for(int i = 0; i < sockets.size(); i++){
+            Socket s = (Socket)sockets.elementAt(i);
+            if(s != null)
+                writeOut(s, p);
+        }
+    }
+
+
+    public void broadcastRemoveGroup(Vector sockets, String groupName){
+        if(sockets == null || groupName == null || groupName.equals("")) return;
+
+//        System.out.println("broadcast remove group");
+        Packet p = new Packet(Packet.MSGTYPE_BROADCAST_REMOVE_GROUP);
+        p.setGroupname(groupName);
+
+        for(int i = 0; i < sockets.size(); i++){
+            Socket s = (Socket) sockets.elementAt(i);
+            if(s != null)
+                writeOut(s, p);
+        }
+    }
+
+    public void broadcastChatMessage(Vector sockets, String message, String groupName, String userName){
+        if(sockets == null || groupName == null || groupName.equals("") || userName == null || userName.equals("") || message == null|| message.equals("")) return;
+
+//        System.out.println("broadcast chat message");
+        Packet p = new Packet(Packet.MSGTYPE_BROADCAST_CHAT_MESSAGE);
+        p.setGroupname(groupName);
+        p.setUsername(userName);
+        p.setChatMessage(message);
+
+        for(int i = 0; i < sockets.size(); i++){
+            Socket s = (Socket)sockets.elementAt(i);
+            if(s != null)
+                writeOut(s, p);
+        }
+    }
+
+    /* Sign off */
+    // TESTED
+    public void signOffOK(Socket s){
+        if (s == null) return;
+//        System.out.println("Leaving System OK");
+        Packet p = new Packet(Packet.MSGTYPE_SIGN_OUT);
+        p.setStatusCode(Packet.SUCCESS);
+        writeOut(s, p);
+    }
+}
diff --git a/retina/chat/server/ServerSocketListenerThread.java b/retina/chat/server/ServerSocketListenerThread.java
new file mode 100644
index 0000000..d51557f
--- /dev/null
+++ b/retina/chat/server/ServerSocketListenerThread.java
@@ -0,0 +1,36 @@
+package retina.chat.server;
+
+import java.net.*;
+import java.io.*;
+
+public class ServerSocketListenerThread extends Thread{
+    ServerSocket server;
+    ChatServer leder;
+
+    /** Creates a new instance of ServerSocketListenerThread */
+    public ServerSocketListenerThread(ChatServer L, ServerSocket s) {
+        server = s;
+        leder = L;
+    }
+
+    /**
+     *  Function: run
+     *  Desc: wait for socket requests and grant
+     */
+    public void run(){
+//        System.out.println("Running server socket");
+        while(true){
+            try{
+                Socket s = server.accept();
+//                System.out.println("Found a request");
+                leder.addSocket(s);
+                UserSocketListenerThread user = new UserSocketListenerThread(leder, s);
+                user.start();
+//                System.out.println("Started user thread");
+            }catch(Exception e){
+                System.out.println("*****Error happened in Server Listener Thread");
+               // e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/retina/chat/server/User.java b/retina/chat/server/User.java
new file mode 100644
index 0000000..5cd5998
--- /dev/null
+++ b/retina/chat/server/User.java
@@ -0,0 +1,58 @@
+package retina.chat.server;
+
+import java.util.*;
+
+public class User {
+    String userName;
+    Vector groups;
+
+    /** Creates a new instance of User */
+    public User(String name) {
+        userName = name;
+        groups = new Vector();
+    }
+
+    /**
+     *  Function: getName
+     *  Desc: returns the usr name
+     */
+    public String getName(){
+        return userName;
+    }
+
+    /**
+     *  Function: inGroup
+     *  Desc: returns if the user is in a group
+     */
+    public boolean isInGroup(Group group){
+        if(group != null)
+            return groups.contains(group);
+
+        return false;
+    }
+
+    /**
+     *  Function: addGroup
+     *  Desc: adds a group to the user group vector
+     */
+    public void addGroup(Group group){
+        if(group != null && !groups.contains(group)){
+            groups.addElement(group);
+        }
+    }
+
+    /**
+     *  Function: removeGroup
+     *  Desc: remove a user from a group
+     */
+    public void removeGroup(Group group){
+        if(group != null && groups.contains(group)){
+            groups.removeElement(group);
+        }
+    }
+
+    public String toString()
+    {
+	return userName;
+    }
+}
diff --git a/retina/chat/server/UserSocketListenerThread.java b/retina/chat/server/UserSocketListenerThread.java
new file mode 100644
index 0000000..9822f26
--- /dev/null
+++ b/retina/chat/server/UserSocketListenerThread.java
@@ -0,0 +1,50 @@
+package retina.chat.server;
+
+
+import java.net.*;
+import java.util.*;
+import java.io.*;
+
+import retina.chat.common.Packet;
+
+public class UserSocketListenerThread extends Thread{
+       Socket socket;
+       String userName;
+       ChatServer leder;
+
+    /** Creates a new instance of UserSocketListenerThread */
+    public UserSocketListenerThread(ChatServer L, Socket s) {
+        socket = s;
+        leder = L;
+        userName = "";
+    }
+
+    /**
+     *  Function: setUserName
+     *  Desc: sets the username that belongs to the socket/thread
+     */
+    public void setUserName(String name){
+        userName = name;
+    }
+
+    public void run(){
+        //System.out.println("Running a user socket");
+        ObjectInput input = null;
+        try{
+            boolean ok = true;
+            while(ok){
+                input = new ObjectInputStream(socket.getInputStream());
+                Packet packet = (Packet)input.readObject();
+                if(packet.getMessageType() == Packet.MSGTYPE_SIGN_OUT){
+                    ok = false;
+                }
+                //System.out.println("I got a packet " + packet.getMessageType());
+                Message m = new Message(socket, packet);
+                leder.queue(m);
+            }
+        }catch(Exception e){
+            System.out.println("*****Error happened in User Listener Thread");
+           // e.printStackTrace();
+        }
+    }
+}
diff --git a/retina/common/CompilationErrorEvent.java b/retina/common/CompilationErrorEvent.java
index 00a5068..239f8fa 100644
--- a/retina/common/CompilationErrorEvent.java
+++ b/retina/common/CompilationErrorEvent.java
@@ -1,35 +1 @@
-package retina.common;
-/***********************************************
- *
- * This class represents one compilation error recorded by the system.
- *
- ***********************************************/
-public class CompilationErrorEvent extends Event
-{
-	/**
-	 * The actual error represented by this event. Something like '; expected'.
-	 */
-	private String error;
-
-	/**
-	 * The message received by the compilation error. This would provide additional
-	 * information beyond just what the error itself is.
-	 */
-	private String message;
-	public CompilationErrorEvent(String user, String assignment, String time, String err, String msg)
-	{
-		super(user, assignment, time);
-		error = err;
-		message = msg;
-	}
-	public String getError()
-	{
-		return error;
-	}
-
-	public String getMessage()
-	{
-		return message;
-	}
-
-}
+package retina.common;
/***********************************************
 *
 * This class represents one compilation error recorded by the system.
 *
 ***********************************************/

public class CompilationErrorEvent extends Event
{
	/**
	 * The actual error represented by this event. Something like '; expected'. 
	 */
	private String error;
	
	/**
	 * The file in which the error occurred.
	 */
	private String file;
	
	/**
	 * The line number on which the error occurred.
	 */
	private String line;
	
	/**
	 * The message received by the compilation error. This would provide additional
	 * information beyond just what the error itself is.
	 */
	private String message;

	public CompilationErrorEvent(String user, String assignment, String time, String err, String msg, String fileName, String lineNum)
	{
		super(user, assignment, time);
		error = err;
		message = msg;
		file = fileName;
		line = lineNum;
	}

	public String getError()
	{
		return error;
	}
	
	public String getMessage()
	{
		return message;
	}
	
	public String getFile()
	{
		return file;
	}
	
	public String getLine()
	{
		return line;
	}
	
	public String toString()
	{
		return user + ":" + time + ":" + error;
	}
}
diff --git a/retina/common/CompilationEvent.java b/retina/common/CompilationEvent.java
index 0b42536..828a28e 100644
--- a/retina/common/CompilationEvent.java
+++ b/retina/common/CompilationEvent.java
@@ -13,14 +13,14 @@ package retina.common;
 public class CompilationEvent extends Event
 {
 	/**
-	 * Whether or not this compilation was successful, i.e. no errors occurred.
+	 * The number of errors made during this compilation attempt.
 	 */
-	private boolean success;
+	private int errors;

-	public CompilationEvent(String user, String assignment, String time, boolean s)
+	public CompilationEvent(String user, String assignment, String time, int e)
 	{
 		super(user, assignment, time);
-		success = s;
+		errors = e;
 	}

 	/**
@@ -28,7 +28,22 @@ public class CompilationEvent extends Event
 	 */
 	public boolean isSuccessful()
 	{
-		return success;
+		return errors == 0;
+	}
+
+	/**
+	 * Returns the number of errors that were made.
+	 */
+	public int getErrors()
+	{
+		return errors;
 	}

+	/**
+	 * Prints out the value of this object
+	 */
+	public String toString()
+	{
+		return user + " at " + time + " " + errors + " errors";
+	}
 }
diff --git a/retina/common/Event.java b/retina/common/Event.java
index db01b56..244f00f 100644
--- a/retina/common/Event.java
+++ b/retina/common/Event.java
@@ -1,51 +1 @@
-package retina.common;
-/***********************************************
- *
- * This class is the base class for representing one event in the system.
- * It is abstract because we should never create just an "event", it should
- * always be a specific type of event.
- *
- ***********************************************/
-public abstract class Event
-{
-	/**
-	 * The user id.
-	 */
-	private String user;
-	/**
-	 * The assignment.
-	 */
-	private String assignment;
-	/**
-	 * The time in MM-DD-YY-hh:mm:ss format.
-	 * TODO: we may want to use a Date object instead
-	 */
-	private String time;
-	public Event(String user, String assignment, String time)
-	{
-		this.user = user;
-		this.assignment = assignment;
-		this.time = time;
-	}
-	/**
-	 * Returns the value of the user id.
-	 */
-	public String getUser()
-	{
-		return user;
-	}
-	/**
-	 * Returns the value of the assignment id.
-	 */
-	public String getAssignment()
-	{
-		return assignment;
-	}
-	/**
-	 * Returns the value of the event time.
-	 */
-	public String getTime()
-	{
-		return time;
-	}
-}
\ No newline at end of file
+package retina.common;
/***********************************************
 *
 * This class is the base class for representing one event in the system.
 * It is abstract because we should never create just an "event", it should
 * always be a specific type of event.
 *
 ***********************************************/

public abstract class Event
{
	/**
	 * The user id.
	 */
	protected String user;

	/**
	 * The assignment.
	 */
	protected String assignment;

	/**
	 * The time in YY-MM-DD-hh:mm:ss format.
	 * TODO: we may want to use a Date object instead
	 */
	protected String time;

	public Event(String user, String assignment, String time)
	{
		this.user = user;
		this.assignment = assignment;
		this.time = time;
	}

	/**
	 * Returns the value of the user id.
	 */
	public String getUser()
	{
		return user;
	}

	/**
	 * Returns the value of the assignment id.
	 */
	public String getAssignment()
	{
		return assignment;
	}

	/**
	 * Returns the value of the event time.
	 */
	public String getTime()
	{
		return time;
	}

}
\ No newline at end of file
diff --git a/retina/common/Logger.java b/retina/common/Logger.java
new file mode 100644
index 0000000..de68e16
--- /dev/null
+++ b/retina/common/Logger.java
@@ -0,0 +1,80 @@
+package retina.common;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.Date;
+
+/**
+ * The point of this class is just to have static methods for logging stuff to files.
+ *
+ */
+
+
+public class Logger
+{
+	/* Names of files */
+	private final static String ERROR_LOG = "error.log";
+	private final static String WARNING_LOG = "warning.log";
+	private final static String INFO_LOG = "info.log";
+
+	/* Whether or not to log the different types of messages */
+	private static boolean isLogError = true;
+	private static boolean isLogWarning = true;
+	private static boolean isLogInfo = true;
+
+	/**********************************************************
+	 *
+	 * Don't touch anything below here!!!!
+	 *
+	 **********************************************************/
+
+
+	private static PrintWriter errorLogFile;
+	private static PrintWriter warningLogFile;
+	private static PrintWriter infoLogFile;
+
+	public static boolean isLogError() { return isLogError; }
+	public static boolean isLogWarning() { return isLogWarning; }
+	public static boolean isLogInfo() { return isLogInfo; }
+
+	public static void logError(String msg)
+	{
+		if (errorLogFile == null)
+		{
+			// TODO: backup the original
+			try { errorLogFile = new PrintWriter(new File(ERROR_LOG)); }
+			catch (Exception e) { }
+		}
+		log(errorLogFile, msg);
+	}
+
+	public static void logWarning(String msg)
+	{
+		if (warningLogFile == null)
+		{
+			// TODO: backup the original
+			try { warningLogFile = new PrintWriter(new File(WARNING_LOG)); }
+			catch (Exception e) { }
+		}
+		log(warningLogFile, msg);
+	}
+
+	public static void logInfo(String msg)
+	{
+		if (infoLogFile == null)
+		{
+			// TODO: backup the original
+			try { infoLogFile = new PrintWriter(new File(INFO_LOG)); }
+			catch (Exception e) { }
+		}
+		log(infoLogFile, msg);
+	}
+
+	private static void log(PrintWriter out, String msg)
+	{
+		Date now = new Date();
+		out.println(now + ": " + msg);
+		out.flush();
+	}
+
+}
diff --git a/retina/common/OccurrenceMap.java b/retina/common/OccurrenceMap.java
new file mode 100644
index 0000000..1b8dc69
--- /dev/null
+++ b/retina/common/OccurrenceMap.java
@@ -0,0 +1,15 @@
+package retina.common;
+
+/**
+ * This is just a class that holds an array of Strings and an array of ints. The nth string
+ * is mapped to the nth int, indicating that it has that number of occurrences.
+ */
+
+
+public class OccurrenceMap
+{
+	public String[] keys;
+	public int[] occurrences;
+
+	public int size() { return keys.length; }
+}
diff --git a/retina/common/Student.java b/retina/common/Student.java
new file mode 100644
index 0000000..b7d0e96
--- /dev/null
+++ b/retina/common/Student.java
@@ -0,0 +1,27 @@
+package retina.common;
+/**
+ *
+ * Represents a student in the system.
+ *
+ */
+public class Student
+{
+	private String uni;
+	private String name;
+
+	public Student(String u, String n)
+	{
+		uni = u;
+		name = n;
+	}
+
+	public String getUni()
+	{
+		return uni;
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+}
diff --git a/retina/db/CompilationErrorEventManager.java b/retina/db/CompilationErrorEventManager.java
new file mode 100644
index 0000000..aef1ced
--- /dev/null
+++ b/retina/db/CompilationErrorEventManager.java
@@ -0,0 +1,298 @@
+package retina.db;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+import retina.common.CompilationErrorEvent;
+import retina.common.OccurrenceMap;
+
+public class CompilationErrorEventManager extends CompilationEventManager
+{
+
+
+	/**
+	 * This method inserts an Event into the compilation_errors table.
+	 * That table has columns called student, assignment, date, and error.
+	 * Return false if there is a database error.
+	 */
+	public boolean insertCompilationErrorEvent(CompilationErrorEvent e)
+	{
+		try
+		{
+			// get a database connection
+			con = getConnection();
+
+			if(con != null)
+			{
+				// create a PreparedStatement
+				PreparedStatement stmt = con.prepareStatement("INSERT INTO compilation_errors (student, assignment, date, error, message, filename, line) VALUES (?, ?, ?, ?, ?, ?, ?)");
+
+				// now create the SQL query
+				// String query = "INSERT INTO compilation_errors (student, assignment, date, error, message) VALUES ('" + e.getUser() + "', '" + assignment + "', '" + e.getTime() + "', '" + e.getError() + "', '" + e.getMessage() + "')";
+
+				stmt.setString(1, e.getUser());
+				stmt.setString(2, e.getAssignment());
+				stmt.setString(3, e.getTime());
+				stmt.setString(4, e.getError());
+				stmt.setString(5, e.getMessage());
+				stmt.setString(6, e.getFile());
+				stmt.setString(7, e.getLine());
+
+				// execute the query
+				stmt.executeUpdate();
+
+			}
+			else System.out.println("Error: No active Connection");
+
+		}
+		catch(Exception error)
+		{
+			error.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+
+		return true;
+	}
+
+
+
+	/**
+	 * This method selects all CompilationErrorEvents
+	 * for the given user id and assignment id. It will
+	 * return null if the user id or assignment id is invalid.
+	 * It will throw an Exception if a database error occurs.
+	 */
+	public CompilationErrorEvent[] getCompilationErrorEvents(String user, String assignment)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "SELECT date, error, message, filename, line FROM compilation_errors WHERE student = '" + user + "' AND assignment = '" + assignment + "' ORDER BY date";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// we can't actually know the size of the ResultSet in advance
+				// so we can't create an array... yet
+				// use an ArrayList for now
+				ArrayList<CompilationErrorEvent> results = new ArrayList<CompilationErrorEvent>();
+
+				// iterate through the ResultSet and populate the ArrayList
+				int count = 0;
+				while (rs.next())
+				{
+					// get the "assignment" column for this particular result
+					String date = rs.getString("date");
+					String error = rs.getString("error");
+					String message = rs.getString("message");
+					String file = rs.getString("filename");
+					String line = rs.getString("line");
+					results.add(new CompilationErrorEvent(user, assignment, date, error, message, file, line));
+					count++;
+				}
+
+				// now return the results
+				return results.toArray(new CompilationErrorEvent[count]);
+
+
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+	}
+
+	/**
+	 * This method gets the most common error for the particular student and assignment.
+	 */
+	public CompilationErrorEvent getMostCommonCompilationError(String user, String assignment)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "SELECT error, count(*) FROM compilation_errors WHERE student = '" + user + "' and assignment = '" + assignment +"' GROUP BY error ORDER BY count(*) DESC";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// TODO: what if there's more than one?
+				if (rs.next())
+				{
+					// get the "assignment" column for this particular result
+					String error = rs.getString("error");
+					return new CompilationErrorEvent(user, assignment, "N/A", error, null, null, null);
+				}
+				else
+					return null;
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+	}
+
+
+	/**
+	 * This method gets the most common errors for ALL students for the given assignment.
+	 */
+	public OccurrenceMap getMostCommonCompilationErrors(String assignment, int num)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "SELECT error, count(*) FROM compilation_errors WHERE assignment = '" + assignment + "' GROUP BY error ORDER BY count(*) DESC";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// create the arrays
+				OccurrenceMap map = new OccurrenceMap();
+				map.keys = new String[num];
+				map.occurrences = new int[num];
+
+				// counts how many objects have been placed in the arrays
+				int counter = 0;
+
+				while (counter < num && rs.next())
+				{
+					map.keys[counter] = rs.getString(1);
+					map.occurrences[counter] = rs.getInt(2);
+					counter++;
+				}
+
+				return map;
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+	}
+
+	/**
+	 * For the specified assignment, this returns an OccurrenceMap that details how many compilation errors occurred
+	 * at different times of the day.
+	 */
+	public OccurrenceMap getCompilationErrorTimes(String assignment)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "select datepart(mm, date), datepart(d, date), datepart(hh, date), count(*) from compilation_errors where assignment = '" + assignment + "' group by datepart(mm, date), datepart(d, date), datepart(hh, date)";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// we can't actually know the size of the ResultSet in advance
+				// so we can't create an array... yet
+				// use an ArrayList for now
+				// TODO: can we be certain that the ArrayList will keep everything in the right order?
+				ArrayList<String> times = new ArrayList<String>();
+				ArrayList<Integer> events = new ArrayList<Integer>();
+
+				// iterate through the ResultSet and populate the ArrayList
+				int count = 0;
+				while (rs.next())
+				{
+					String month = rs.getString(1);
+					String day = rs.getString(2);
+					String hour = rs.getString(3);
+					String date = month + "-" + day + " " + hour + ":00";
+					times.add(date);
+
+					int compile = rs.getInt(4);
+					events.add(compile);
+
+					count++;
+				}
+
+				// now return the results
+				OccurrenceMap map = new OccurrenceMap();
+				map.keys = times.toArray(new String[count]);
+				map.occurrences = new int[count];
+				for (int i = 0; i < count; i++)
+				{
+					map.occurrences[i] = events.get(i).intValue();
+				}
+
+				return map;
+
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+
+	}
+
+
+	public static void main(String[] args)
+	{
+		CompilationErrorEventManager m = new CompilationErrorEventManager();
+		OccurrenceMap map = m.getCompilationErrorTimes("1");
+		for (int i = 0; i < map.keys.length; i++)
+		{
+			System.out.println(map.keys[i] + " " + map.occurrences[i]);
+		}
+	}
+}
diff --git a/retina/db/CompilationEventManager.java b/retina/db/CompilationEventManager.java
new file mode 100644
index 0000000..8e9d22c
--- /dev/null
+++ b/retina/db/CompilationEventManager.java
@@ -0,0 +1,236 @@
+package retina.db;
+
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+
+import retina.common.CompilationEvent;
+import retina.common.OccurrenceMap;
+
+/**
+ * This class holds all methods for getting info related to compilation events.
+ *
+ */
+
+
+public class CompilationEventManager extends DatabaseManager
+{
+	/**
+	 * This method inserts an Event into the compilations table.
+	 * That table has columns called student, assignment, date, and success.
+	 * Return false if there is a database error.
+	 */
+	public boolean insertCompilationEvent(CompilationEvent e)
+	{
+		try
+		{
+			// get a database connection
+			con = getConnection();
+
+			if(con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "INSERT INTO compilations (student, assignment, date, errors) VALUES ('" + e.getUser() + "', '" + e.getAssignment() + "', '" + e.getTime() + "', '" + e.getErrors() + "')";
+
+				// execute the query
+				stmt.executeUpdate(query);
+				//System.out.println("Executed " + query);
+			}
+			else System.out.println("Error: No active Connection");
+
+			// we need to add this student if he/she doesn't already exist
+			if (getStudentName(e.getUser()) == null)
+			{
+				insertStudent(e.getUser(), "New Student");
+				//System.out.println("Inserted " + e.getUser());
+			}
+
+		}
+		catch(Exception error)
+		{
+			error.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+
+		return true;
+	}
+
+
+
+	/**
+	 * This method selects all CompilationEvents
+	 * for the given user id and assignment id. It will
+	 * return null if the user id or assignment id is invalid.
+	 * It will throw an Exception if a database error occurs.
+	 */
+	public CompilationEvent[] getCompilationEvents(String user, String assignment)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "SELECT date, errors FROM compilations WHERE student = '" + user + "' AND assignment = '" + assignment + "' ORDER BY date";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// we can't actually know the size of the ResultSet in advance
+				// so we can't create an array... yet
+				// use an ArrayList for now
+				ArrayList<CompilationEvent> results = new ArrayList<CompilationEvent>();
+
+				// iterate through the ResultSet and populate the ArrayList
+				int count = 0;
+				while (rs.next())
+				{
+					// get the "assignment" column for this particular result
+					String date = rs.getString("date");
+					int errors = rs.getInt("errors");
+					results.add(new CompilationEvent(user, assignment, date, errors));
+					count++;
+				}
+
+				// now return the results
+				return results.toArray(new CompilationEvent[count]);
+
+
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+	}
+
+
+
+	/**
+	 * This method returns the total number of compilations for a user on an assignment.
+	 */
+	public int getTotalNumberOfCompilations(String user, String assignment)
+	{
+		CompilationEvent[] events = getCompilationEvents(user, assignment);
+		if (events != null) return events.length;
+		else return 0;
+	}
+
+	/**
+	 * This method returns the number of successful compilations for a user on an assignment.
+	 */
+	public int getNumberOfSuccessfulCompilations(String user, String assignment)
+	{
+		CompilationEvent[] events = getCompilationEvents(user, assignment);
+		if (events != null)
+		{
+			int total = 0;
+			for (CompilationEvent e : events)
+			{
+				if (e.isSuccessful()) total++;
+			}
+			return total;
+		}
+		else return 0;
+
+
+	}
+
+
+	/**
+	 * For the specified assignment, this returns an OccurrenceMap that details how many compilations occurred
+	 * at different times of the day.
+	 */
+	public OccurrenceMap getCompilationTimes(String assignment)
+	{
+		try
+		{
+			con = getConnection();
+
+			if (con != null)
+			{
+				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				String query = "select datepart(mm, date), datepart(d, date), datepart(hh, compilations.date), count(*) from compilations where assignment = '" + assignment + "' group by datepart(mm, date), datepart(d, date), datepart(hh, compilations.date)";
+
+				// execute the query
+				ResultSet rs = stmt.executeQuery(query);
+				//System.out.println("Executed " + query);
+
+				// we can't actually know the size of the ResultSet in advance
+				// so we can't create an array... yet
+				// use an ArrayList for now
+				// TODO: can we be certain that the ArrayList will keep everything in the right order?
+				ArrayList<String> times = new ArrayList<String>();
+				ArrayList<Integer> events = new ArrayList<Integer>();
+
+				// iterate through the ResultSet and populate the ArrayList
+				int count = 0;
+				while (rs.next())
+				{
+					String month = rs.getString(1);
+					String day = rs.getString(2);
+					String hour = rs.getString(3);
+					String date = month + "-" + day + " " + hour + ":00";
+					times.add(date);
+
+					int compile = rs.getInt(4);
+					events.add(compile);
+
+					count++;
+				}
+
+				// now return the results
+				OccurrenceMap map = new OccurrenceMap();
+				map.keys = times.toArray(new String[count]);
+				map.occurrences = new int[count];
+				for (int i = 0; i < count; i++)
+				{
+					map.occurrences[i] = events.get(i).intValue();
+				}
+
+				return map;
+
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		finally
+		{
+			closeConnection();
+		}
+		return null;
+
+	}
+
+	public static void main(String[] args)
+	{
+		CompilationEventManager m = new CompilationEventManager();
+		OccurrenceMap map = m.getCompilationTimes("1");
+		for (int i = 0; i < map.keys.length; i++)
+		{
+			System.out.println(map.keys[i] + " " + map.occurrences[i]);
+		}
+	}
+
+}
diff --git a/retina/db/DatabaseConnector.java b/retina/db/DatabaseConnector.java
index 3103542..6bd31c6 100644
--- a/retina/db/DatabaseConnector.java
+++ b/retina/db/DatabaseConnector.java
@@ -1,91 +1,14 @@
-package retina.db;
-/**
- * This class has all the base functionality for managing a database connection, like
- * connecting and closing it.
- */
-
+package retina.db;

/**
 * This class has all the base functionality for managing a database connection, like
 * connecting and closing it.
 */
 public class DatabaseConnector
-{
-
-	protected java.sql.Connection  con = null;
-
-	private final String url = "jdbc:sqlserver://";
-	private final String serverName= "vesey.cs.columbia.edu";
-	private final String portNumber = "1433";
-	private final String databaseName= "Retina";
-	private final String userName = "student";
-	private final String password = "password";
-	private final String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
-    // Constructor
-    public DatabaseConnector(){}
-    /* This method just creates the connection string based on all the properties. */
-    private String getConnectionUrl()
-    {
-    	// return url+serverName+":"+portNumber+";databaseName="+databaseName+";selectMethod="+selectMethod+";";
-    	return url+serverName+":"+portNumber+";user="+userName+";password="+password+";databaseName="+databaseName;
-    }
-    /* Create the database connection */
-    protected java.sql.Connection getConnection()
-    {
-    	try
-    	{
-    		Class.forName(driver);
-    		con = java.sql.DriverManager.getConnection(getConnectionUrl(),userName,password);
-            //if(con!=null) System.out.println("Connection Successful!");
-    	}
-    	catch (Exception e)
-    	{
-    		System.out.println("Error Trace in getConnection() : " + e.getMessage());
-    		e.printStackTrace();
-    	}
-        return con;
-    }
-
-    /* Close the connection to the database. */
-    protected void closeConnection()
-    {
-    	try
-    	{
-    		if (con != null) con.close();
-        }
-    	catch (Exception e)
-    	{
-    		// if an error occurs, well, what can you do?
-    	}
-    	finally
-    	{
-    		con = null;
-    	}
-    }
-    /* Test method to display the driver properties and database details */
-    public void displayDbProperties()
-    {
-    	java.sql.DatabaseMetaData dm = null;
-    	java.sql.ResultSet rs = null;
-    	try{
-    		con= this.getConnection();
-    		if(con!=null){
-    			dm = con.getMetaData();
-    			System.out.println("Driver Information");
-    			System.out.println("\tDriver Name: "+ dm.getDriverName());
-    			System.out.println("\tDriver Version: "+ dm.getDriverVersion ());
-    			System.out.println("\nDatabase Information ");
-    			System.out.println("\tDatabase Name: "+ dm.getDatabaseProductName());
-    			System.out.println("\tDatabase Version: "+ dm.getDatabaseProductVersion());
-    			System.out.println("Avalilable Catalogs ");
-    			rs = dm.getCatalogs();
-    			while(rs.next()){
-    				System.out.println("\tcatalog: "+ rs.getString(1));
-    			}
-    			rs.close();
-    			rs = null;
-    			closeConnection();
-    		}else System.out.println("Error: No active Connection");
-    	}catch(Exception e){
-    		e.printStackTrace();
-    	}
-    	dm=null;
-    }
+{
+	protected java.sql.Connection  con = null;
     
	private final String url = "jdbc:sqlserver://";
	private final String serverName= "vesey.cs.columbia.edu";
	private final String portNumber = "1433";
	private final String databaseName= "Retina";
	private final String userName = "student";
	private final String password = "password";
	private final String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
+    // Constructor
    public DatabaseConnector(){}
+
    /* This method just creates the connection string based on all the properties. */
+    private String getConnectionUrl()
    {
    	// return url+serverName+":"+portNumber+";databaseName="+databaseName+";selectMethod="+selectMethod+";";
    	return url+serverName+":"+portNumber+";user="+userName+";password="+password+";databaseName="+databaseName;
    }
+
    /* Create the database connection */
    protected java.sql.Connection getConnection()
    {
    	try
    	{
    		Class.forName(driver);
    		con = java.sql.DriverManager.getConnection(getConnectionUrl(),userName,password);
            //if(con!=null) System.out.println("Connection Successful!");
    	}
    	catch (Exception e)
    	{
    		System.out.println("Error Trace in getConnection() : " + e.getMessage());
    		e.printStackTrace();
    	}
        return con;
    }
+
+    /* Close the connection to the database. */
    protected void closeConnection()
    {
    	try
    	{
    		if (con != null) con.close();
        }	
    	catch (Exception e)
    	{
    		// if an error occurs, well, what can you do?
    	}
    	finally
    	{
    		con = null;
    	}
    }
+
    /* Test method to display the driver properties and database details */
    public void displayDbProperties()
    {
    	java.sql.DatabaseMetaData dm = null;
    	java.sql.ResultSet rs = null;
    	try{
    		con= this.getConnection();
    		if(con!=null){
    			dm = con.getMetaData();
    			System.out.println("Driver Information");
    			System.out.println("\tDriver Name: "+ dm.getDriverName());
    			System.out.println("\tDriver Version: "+ dm.getDriverVersion ());
    			System.out.println("\nDatabase Information ");
    			System.out.println("\tDatabase Name: "+ dm.getDatabaseProductName());
    			System.out.println("\tDatabase Version: "+ dm.getDatabaseProductVersion());
    			System.out.println("Avalilable Catalogs ");
    			rs = dm.getCatalogs();
    			while(rs.next()){
    				System.out.println("\tcatalog: "+ rs.getString(1));
    			}	
    			rs.close();
    			rs = null;
    			closeConnection();
    		}else System.out.println("Error: No active Connection");
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	dm=null;
    }
 	public static void main(String[] args)
 	{
 		DatabaseConnector dc = new DatabaseConnector();
diff --git a/retina/db/DatabaseManager.java b/retina/db/DatabaseManager.java
index 442999e..f4be0af 100644
--- a/retina/db/DatabaseManager.java
+++ b/retina/db/DatabaseManager.java
@@ -8,32 +8,21 @@ package retina.db;
  */

 import java.sql.*;
-import java.util.Scanner;
 import java.util.ArrayList;

-import retina.common.CompilationErrorEvent;
-import retina.common.CompilationEvent;
-import retina.common.Event;

-public class DatabaseManager extends DatabaseConnector implements DatabaseReader, DatabaseWriter
-{
-	// Constructor
-	public DatabaseManager(){}
+public class DatabaseManager extends DatabaseConnector
+{
	// Constructor
	public DatabaseManager(){}

 	/**
 	 * This is just a toy method to insert into the "students" table.
 	 * Use this as a starting point for any "insert" method.
 	 * @return true if the insert succeeded, false otherwise
 	 **/
-	public boolean insertStudent(String uni, String name)
-	{
-		try
+	public boolean insertStudent(String uni, String name)
	{
		try
 		{
-			// get a database connection
-			con = getConnection();
-			if(con != null)
-			{
-				// create a Statement
+			// get a database connection
			con = getConnection();
+			if(con != null)
			{
				// create a Statement
 				Statement stmt = con.createStatement();

 				// now create the SQL query
@@ -45,21 +34,16 @@ public class DatabaseManager extends DatabaseConnector implements DatabaseReader
 				//System.out.println("Executed " + query);

 				return true;
-            }
-            else System.out.println("Error: No active Connection");
-        }
-        catch(Exception e)
-        {
-			e.printStackTrace();
-		}
+            }
            else System.out.println("Error: No active Connection");
+        }
        catch(Exception e)
        {
			e.printStackTrace();
		}
 		finally
 		{
 			closeConnection();
 		}

 		// if we get here, the command did not succeed
-		return false;
-	}
+		return false;
+	}

 	/**
 	 * This is just a toy method to read from the "students" table.
@@ -256,364 +240,4 @@ public class DatabaseManager extends DatabaseConnector implements DatabaseReader
 	}


-
-	/**
-	 * This method inserts an event into the appropriate
-	 * table in the database. If successful, it returns true.
-	 * If an error occurs, it returns false.
-	 */
-	public boolean insertEvent(Event e)
-	{
-		if (e instanceof CompilationErrorEvent)
-		{
-			return insertCompilationErrorEvent((CompilationErrorEvent)e);
-		}
-		else if (e instanceof CompilationEvent)
-		{
-			return insertCompilationEvent((CompilationEvent)e);
-		}
-		return true;
-	}
-
-	/**
-	 * This method inserts an Event into the compilation_errors table.
-	 * That table has columns called student, assignment, date, and error.
-	 * Return false if there is a database error.
-	 */
-	private boolean insertCompilationErrorEvent(CompilationErrorEvent e)
-	{
-		try
-		{
-			// get a database connection
-			con = getConnection();
-
-			if(con != null)
-			{
-				// create a PreparedStatement
-				PreparedStatement stmt = con.prepareStatement("INSERT INTO compilation_errors (student, assignment, date, error, message) VALUES (?, ?, ?, ?, ?)");
-
-				// now create the SQL query
-				// String query = "INSERT INTO compilation_errors (student, assignment, date, error, message) VALUES ('" + e.getUser() + "', '" + assignment + "', '" + e.getTime() + "', '" + e.getError() + "', '" + e.getMessage() + "')";
-
-				stmt.setString(1, e.getUser());
-				stmt.setString(2, e.getAssignment());
-				stmt.setString(3, e.getTime());
-				stmt.setString(4, e.getError());
-				stmt.setString(5, e.getMessage());
-
-				// execute the query
-				stmt.executeUpdate();
-
-			}
-			else System.out.println("Error: No active Connection");
-
-		}
-		catch(Exception error)
-		{
-			error.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-
-		return true;
-	}
-
-
-	/**
-	 * This method inserts an Event into the compilations table.
-	 * That table has columns called student, assignment, date, and success.
-	 * Return false if there is a database error.
-	 */
-	private boolean insertCompilationEvent(CompilationEvent e)
-	{
-		try
-		{
-			// get a database connection
-			con = getConnection();
-
-			if(con != null)
-			{
-				// create a Statement
-				Statement stmt = con.createStatement();
-
-				// now create the SQL query
-				String success = e.isSuccessful() ? "TRUE" : "FALSE";
-
-				String query = "INSERT INTO compilations (student, assignment, date, success) VALUES ('" + e.getUser() + "', '" + e.getAssignment() + "', '" + e.getTime() + "', '" + success + "')";
-
-				// execute the query
-				stmt.executeUpdate(query);
-				//System.out.println("Executed " + query);
-			}
-			else System.out.println("Error: No active Connection");
-
-		}
-		catch(Exception error)
-		{
-			error.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-
-		return true;
-	}
-
-
-	/**
-	 * This method selects all CompilationErrorEvents
-	 * for the given user id and assignment id. It will
-	 * return null if the user id or assignment id is invalid.
-	 * It will throw an Exception if a database error occurs.
-	 */
-	public CompilationErrorEvent[] getCompilationErrorEvents(String user, String assignment)
-	{
-		try
-		{
-			con = getConnection();
-
-			if (con != null)
-			{
-				// create a Statement
-				Statement stmt = con.createStatement();
-
-				// now create the SQL query
-				String query = "SELECT date, error, message FROM compilation_errors WHERE student = '" + user + "' AND assignment = '" + assignment + "' ORDER BY date";
-
-				// execute the query
-				ResultSet rs = stmt.executeQuery(query);
-				//System.out.println("Executed " + query);
-
-				// we can't actually know the size of the ResultSet in advance
-				// so we can't create an array... yet
-				// use an ArrayList for now
-				ArrayList<CompilationErrorEvent> results = new ArrayList<CompilationErrorEvent>();
-
-				// iterate through the ResultSet and populate the ArrayList
-				int count = 0;
-				while (rs.next())
-				{
-					// get the "assignment" column for this particular result
-					String date = rs.getString("date");
-					String error = rs.getString("error");
-					String message = rs.getString("message");
-					results.add(new CompilationErrorEvent(user, assignment, date, error, message));
-					count++;
-				}
-
-				// now return the results
-				return results.toArray(new CompilationErrorEvent[count]);
-
-
-			}
-		}
-		catch(Exception e)
-		{
-			e.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-		return null;
-	}
-
-	/**
-	 * This method gets the most common error for the particular student and assignment.
-	 */
-	public CompilationErrorEvent getMostCommonCompilationError(String user, String assignment)
-	{
-		try
-		{
-			con = getConnection();
-
-			if (con != null)
-			{
-				// create a Statement
-				Statement stmt = con.createStatement();
-
-				// now create the SQL query
-				String query = "SELECT error, count(*) FROM compilation_errors WHERE student = '" + user + "' and assignment = '" + assignment +"' GROUP BY error ORDER BY count(*) DESC";
-
-				// execute the query
-				ResultSet rs = stmt.executeQuery(query);
-				//System.out.println("Executed " + query);
-
-				// TODO: what if there's more than one?
-				if (rs.next())
-				{
-					// get the "assignment" column for this particular result
-					String error = rs.getString("error");
-					return new CompilationErrorEvent(user, assignment, "N/A", error, null);
-				}
-				else
-					return null;
-			}
-		}
-		catch(Exception e)
-		{
-			e.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-		return null;
-	}
-
-
-	/**
-	 * This method returns the total number of compilations for a user on an assignment.
-	 */
-	public int getTotalNumberOfCompilations(String user, String assignment)
-	{
-		try
-		{
-			con = getConnection();
-
-			if (con != null)
-			{
-				// create a Statement
-				Statement stmt = con.createStatement();
-
-				// now create the SQL query
-				String query = "SELECT count(*) FROM compilations WHERE student = '" + user + "' and assignment = '" + assignment +"'";
-
-				// execute the query
-				ResultSet rs = stmt.executeQuery(query);
-				//System.out.println("Executed " + query);
-
-				if (rs.next())
-				{
-					// just return the number of compilations
-					return rs.getInt(1);
-				}
-				else
-					return 0;
-			}
-		}
-		catch(Exception e)
-		{
-			e.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-		return 0;
-
-	}
-
-	/**
-	 * This method returns the number of successful compilations for a user on an assignment.
-	 */
-	public int getNumberOfSuccessfulCompilations(String user, String assignment)
-	{
-		try
-		{
-			con = getConnection();
-
-			if (con != null)
-			{
-				// create a Statement
-				Statement stmt = con.createStatement();
-
-				// now create the SQL query
-				String query = "SELECT count(*) FROM compilations WHERE student = '" + user + "' and assignment = '" + assignment +"' and success = 'true'";
-
-				// execute the query
-				ResultSet rs = stmt.executeQuery(query);
-				//System.out.println("Executed " + query);
-
-				if (rs.next())
-				{
-					// just return the number of compilations
-					return rs.getInt(1);
-				}
-				else
-					return 0;
-			}
-		}
-		catch(Exception e)
-		{
-			e.printStackTrace();
-		}
-		finally
-		{
-			closeConnection();
-		}
-		return 0;
-
-
-	}
-
-
-	public static void main(String[] args)
-	{
-		DatabaseManager dm = new DatabaseManager();
-
-		/*
-		// this tests the getCompilationErrorEvents method
-		CompilationErrorEvent[] errors = dm.getCompilationErrorEvents("cdm6", "1");
-		for (CompilationErrorEvent e : errors)
-		{
-			System.out.println(e.getTime() + " " + e.getErrorMessage());
-		}
-		*/
-
-		/*
-		// test the getMostCommonCompilationError method
-		CompilationErrorEvent e = dm.getMostCommonCompilationError("sh2503", "2");
-		System.out.println(e.getErrorMessage());
-		*/
-
-		/*
-		// test the getTotalNumberOfCompilations and getNumberOfSuccessfulCompilations methods
-		int num = dm.getTotalNumberOfCompilations("sh2503", "2");
-		System.out.println("Total " + num);
-		num = dm.getNumberOfSuccessfulCompilations("sh2503", "2");
-		System.out.println("Successful " + num);
-		*/
-
-	}
-
-	/** DON'T RUN THIS METHOD! It's just for testing purposes but it modifies the database! **/
-	private void test()
-	{
-		DatabaseManager myDbTest = new DatabaseManager();
-		CompilationErrorEvent event = new CompilationErrorEvent("hwl2102", "2", "2500", "error", "message");
-
-		// test the insertStudent method
-		Scanner scan = new Scanner(System.in);
-		System.out.print("Enter an ID: ");
-		String id = scan.nextLine();
-		System.out.print("Enter a name: ");
-		String name = scan.nextLine();
-		myDbTest.insertStudent(id, name);
-		System.out.println("Done inserting " + name);
-
-		// test the getStudentnames method
-		String[] students = myDbTest.getStudentNames();
-		System.out.println("Here are the students");
-		for (String student : students) System.out.println(student);
-
-		//test the getAssignments method
-		String[] assignments = myDbTest.getAssignments();
-		System.out.println("Here are the assignments");
-		for(String assignment : assignments) System.out.println(assignment);
-
-		// test the getStudents method
-		String[] unis = myDbTest.getStudents();
-		System.out.println("Here are the unis");
-		for (String uni : unis) System.out.println(uni);
-
-		// test insertComilationError method
-		myDbTest.insertCompilationErrorEvent(event);
-		System.out.println("Done inserting: " + event.getError() + event.getUser() + event.getAssignment() + event.getTime());
-
-
-	}
 }
\ No newline at end of file
diff --git a/retina/db/DatabaseReader.java b/retina/db/DatabaseReader.java
index 64a8089..a747100 100644
--- a/retina/db/DatabaseReader.java
+++ b/retina/db/DatabaseReader.java
@@ -7,7 +7,7 @@ package retina.db;
 import retina.common.CompilationErrorEvent;

 public interface DatabaseReader
-{
+{
 	/**
 	 * This method returns the list of all student IDs.
 	 */
@@ -44,5 +44,4 @@ public interface DatabaseReader
 	 * This method returns the number of successful compilations for a user on an assignment.
 	 */
 	public int getNumberOfSuccessfulCompilations(String user, String assignment);
-
-}
\ No newline at end of file
+	
}
\ No newline at end of file
diff --git a/retina/db/DatabaseWriter.java b/retina/db/DatabaseWriter.java
index 6fd2a30..940c3d5 100644
--- a/retina/db/DatabaseWriter.java
+++ b/retina/db/DatabaseWriter.java
@@ -8,11 +8,5 @@ package retina.db;
 import retina.common.Event;

 public interface DatabaseWriter
-{
-	/**
-	 * This method inserts an event into the appropriate
-	 * table in the database. If successful, it returns true.
-	 * If an error occurs, it returns false.
-	 */
-	public boolean insertEvent(Event e);
+{
	/**
	 * This method inserts an event into the appropriate
	 * table in the database. If successful, it returns true.
	 * If an error occurs, it returns false.
	 */
	public boolean insertEvent(Event e);
 }
\ No newline at end of file
diff --git a/retina/db/LoginEventManager.java b/retina/db/LoginEventManager.java
new file mode 100644
index 0000000..1abf0cb
--- /dev/null
+++ b/retina/db/LoginEventManager.java
@@ -0,0 +1,42 @@
+package retina.db;
+
+/**
+ */
+
+import java.sql.*;
+import java.util.ArrayList;
+
+
+public class LoginEventManager extends DatabaseManager
+{
	// Constructor
	public LoginEventManager(){}
+
+	/**
+	 * This method updates the "logins" table with the student and date.
+	 */
+	public boolean insertLoginEvent(String student, String date, String action)
	{
		try
+		{
+			// get a database connection
			con = getConnection();
+			if(con != null)
			{
				// create a Statement
+				Statement stmt = con.createStatement();
+
+				// now create the SQL query
+				// TODO: use a PreparedStatement instead
+				String query = "INSERT INTO login_events (student, date, action) VALUES ('" + student + "', '" + date + "', '" + action + "')";
+
+				// execute the query
+				stmt.executeUpdate(query);
+				//System.out.println("Executed " + query);
+
+				return true;
+            }
            else System.out.println("Error: No active Connection");
+        }
        catch(Exception e)
        {
			e.printStackTrace();
		}
+		finally
+		{
+			closeConnection();
+		}
+
+		// if we get here, the command did not succeed
+		return false;
+	}
+
+}
\ No newline at end of file
diff --git a/retina/db/LoginEventServer.java b/retina/db/LoginEventServer.java
new file mode 100644
index 0000000..e12d29b
--- /dev/null
+++ b/retina/db/LoginEventServer.java
@@ -0,0 +1,130 @@
+package retina.db;
+/**
+ * This class is the front-end server for getting login events sent from the field and then writing them
+ * to the database.
+ *
+ * TODO: handle errors more gracefully
+ */
+
+import java.io.*;
+import java.net.*;
+import java.util.Scanner;
+
+import retina.im.MessageSender;
+
+
+public class LoginEventServer extends Thread
+{
+    // the server
+    private ServerSocket server;
+
+    /* This is the main method for starting the Server */
+    public static void main(String[] args)
+    {
+    	int port = 1235;
+		if (args.length >= 1)
+		{
+			port = Integer.parseInt(args[0]);
+		}
+		else
+		{
+			System.out.println("Port not specified, using " + port + " as default");
+		}
+
+		LoginEventServer s = new LoginEventServer(port);
+		s.start();
+    }
+
+    /**
+     * Creates a socket server listening on the specified port
+     * @param port the port to listen to
+     */
+    public LoginEventServer(int port)
+    {
+    	init(port);
+    }
+
+    private void init(int port)
+    {
+		try
+		{
+		    server = new ServerSocket(port);
+		    System.out.println("LoginEventServer started... waiting for connection");
+		}
+		catch (Exception e)
+		{
+		    System.out.println("Cannot create LoginEventServer!");
+		    e.printStackTrace();
+		}
+    }
+
+
+    /**
+     * This method does all the work
+     */
+    public void run()
+    {
+    	// TODO: need a graceful shutdown
+    	while (true)
+    	{
+    		try
+    		{
+    			// wait for a client
+    			Socket socket = server.accept();
+
+    			// spin off a new thread
+    			Handler h = new Handler(socket);
+    			h.start();
+    		}
+    		catch (Exception e)
+    		{
+    			e.printStackTrace();
+    		}
+    	}
+    }
+
+    /**
+     * An inner class to do the work of reading the file and writing to the database and such
+     */
+    class Handler extends Thread
+    {
+    	Socket socket = null;
+
+    	Handler(Socket s)
+    	{
+    		socket = s;
+    	}
+
+    	public void run()
+    	{
+    	    // the input stream for reading from the network
+    	    Scanner in = null;
+
+    	    try
+    		{
+    			// get the input stream
+    			in = new Scanner(socket.getInputStream());
+
+    			String student = in.next();
+    			String date = in.next().trim();
+    			String action = in.nextLine().trim();
+
+    		    // the database manager
+    		    LoginEventManager manager = new LoginEventManager();
+    		    manager.insertLoginEvent(student, date, action);
+
+    		    if (action.equals("LOGIN")) System.out.println(student + " logged in at " + date);
+    		    else if (action.equals("LOGOUT")) System.out.println(student + " logged out at " + date);
+    		    else System.out.println(student + " unknown action " + action);
+    		}
+    		catch (Exception e)
+    		{
+    			e.printStackTrace();
+    		}
+    		finally
+    		{
+    		}
+    	}
+    }
+
+}
diff --git a/retina/db/RetinaServer.java b/retina/db/RetinaServer.java
new file mode 100644
index 0000000..e995851
--- /dev/null
+++ b/retina/db/RetinaServer.java
@@ -0,0 +1,132 @@
+package retina.db;
+import javax.swing.Icon;
+
+import retina.im.BotEventHandler;
+import retina.im.MessageSender;
+
+import com.itbs.aimcer.bean.*;
+import com.itbs.aimcer.commune.Connection;
+import com.itbs.aimcer.commune.MessageSupport;
+import com.itbs.aimcer.commune.msn.*;
+import com.itbs.aimcer.commune.smack.*;
+import com.itbs.aimcer.commune.ymsg.*;
+import com.itbs.util.GeneralUtils;
+
+class GroupImplFactory implements GroupFactory {
+            public Group create(String group) {
+                return new GroupImpl();
+            }
+            public Group create(Group group) {
+                return new GroupImpl();
+            }
+			public GroupList getGroupList() {
+				// TODO Auto-generated method stub
+				return null;
+			}
+    }
+    class GroupImpl implements Group {
+        public int size() { return 0; }
+        public void clear(Connection connection) { }
+        public Nameable get(int index) { return null; }
+        public Nameable add(Nameable contact) { return null; }
+        public boolean remove(Nameable contact) { return false; }
+        public String getName() { return "Group"; }
+		public Nameable[] toArray() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+    }
+
+
+    class ContactImplFactory implements ContactFactory {
+            public Contact create(Nameable buddy, Connection connection) {
+                return create(buddy.getName(), connection);
+            }
+
+
+            public Contact create(String name, Connection connection) {
+                return new ContactImpl(connection, name);
+            }
+
+
+            public Contact get(String name, Connection connection) {
+                return new ContactImpl(connection, name);
+            }
+    }
+
+
+    class ContactImpl implements Contact {
+        private final Status status = new StatusImpl(this);
+        private final Connection connection;
+        private final String name;
+
+
+        public ContactImpl(Connection connection, String name) {
+            this.connection = connection;
+            this.name = name;
+        }
+        public void statusChanged() {}
+        public Icon getIcon() { return null; }
+        public void setIcon(Icon icon) {}
+        public Icon getPicture() { return null; }
+        public void setPicture(Icon icon) { }
+        public String getDisplayName() { return name; }
+        public void setDisplayName(String name) {}
+        public Status getStatus() { return status; }
+        public Connection getConnection() { return connection; }
+        public String getName() { return name; }
+    }
+
+
+
+public class RetinaServer {
+
+	public static void main(String[] args) throws Exception
+	{
+
+		/* Initialize shared objects */
+		MessageSender sender = new MessageSender();
+
+		/* Start the IM bot */
+
+		// for Yahoo
+		MessageSupport conn = new YMsgConnection();
+        conn.setUserName("retina_help");
+        conn.setPassword("retina.help123");
+
+        /*
+		// for MSN Messenger
+		MessageSupport conn = new MSNConnection();
+        conn.setUserName("retina.help@hotmail.com");
+        conn.setPassword("columbia.123");
+         */
+
+        conn.assignGroupFactory(new GroupImplFactory());
+        conn.assignContactFactory(new ContactImplFactory());
+
+        try {
+            conn.addEventListener(new BotEventHandler(sender));
+            conn.connect();
+            System.out.println("IM bot connected");
+        } catch (Exception e) {
+            System.out.println("Failed to create required pieces.  Shutting down.");
+            e.printStackTrace();
+            return; // No point waiting if connection isn't available
+        }
+
+        /* Now, start the XmlServer to listen for incoming XML data (like compilations) */
+        XmlServer xs = new XmlServer(1234, sender);
+        xs.start();
+
+        /* Now the server to listen for login events */
+        // TODO: merge this with XmlServer?
+        LoginEventServer ls = new LoginEventServer(1235);
+        ls.start();
+
+        while (true) { // Simplified version of "stick around" wait
+            //System.out.println("waiting");
+            GeneralUtils.sleep(60*60*1000);
+        }
+    } // main()
+
+}
diff --git a/retina/db/XMLLoader.java b/retina/db/XMLLoader.java
index 2064c3a..8d60480 100644
--- a/retina/db/XMLLoader.java
+++ b/retina/db/XMLLoader.java
@@ -21,6 +21,8 @@ import org.xml.sax.SAXException;

 import retina.common.CompilationErrorEvent;
 import retina.common.CompilationEvent;
+import retina.common.Logger;
+import retina.im.MessageSender;

 public class XMLLoader {

@@ -29,9 +31,6 @@ public class XMLLoader {

 	// the set of objects that need to be inserted into the database
 	private ArrayList<CompilationErrorEvent> events;
-
-	// the utility we'll use for writing to the database
-	private DatabaseWriter mgr = new DatabaseManager();

 	// used for sending messages
 	private MessageSender sender;
@@ -63,14 +62,20 @@ public class XMLLoader {
 		parseDocument();

 		// now write all the compilation error events to the database
+		CompilationErrorEventManager mgr = new CompilationErrorEventManager();
 		for (CompilationErrorEvent event : events)
 		{
-			mgr.insertEvent(event);
-			//System.out.println("Inserted");
+			mgr.insertCompilationErrorEvent(event);
+			//System.out.println("Inserted " + event);
 		}

-		// now send a message to the student
-		sender.handleCompilationErrorEvents(events.toArray(new CompilationErrorEvent[events.size()]));
+		// now send a message to the student - temporarily disabled!
+		/*
+		if (events.size() > 0)
+		{
+			sender.handleCompilationErrorEvents(events.toArray(new CompilationErrorEvent[events.size()]));
+		}
+		*/

 	}

@@ -166,7 +171,7 @@ public class XMLLoader {
 				*/
 		}

-		String timeFormat = month + "-" + day + "-" + year.substring(2) + "-" + hour +":" + minutes +":"+ seconds;
+		String timeFormat = year + "-" + month + "-" + day + " " + hour +":" + minutes +":"+ seconds;
 		//System.out.println(timeFormat);


@@ -191,10 +196,10 @@ public class XMLLoader {
 		}

 		// TODO: how do we get the assignment?
-		if (assignment == null) assignment = "1";
+		if (assignment == null) assignment = "3";

-		// records whether or not this was a successful compilation
-		boolean success = true;
+		// tracks the number of errors made
+		int errors = 0;

 		nl = empEl.getElementsByTagName("measure");
 		if(nl != null && nl.getLength() > 0) {
@@ -205,21 +210,26 @@ public class XMLLoader {
 				if (error != null) error = error.trim();
 				String message = getTextValue(e, "message");
 				if (message != null) message = message.trim();
+				String file = getTextValue(e, "file");
+				if (file != null) file = file.trim();
+				String line = getTextValue(e, "line");
+				if (line != null) line = line.trim();

 				//System.out.println("error: " + error.trim());
 				//System.out.println("message: " + message.trim());

-				CompilationErrorEvent errComp = new CompilationErrorEvent(user, assignment, timeFormat, error, message);
+				CompilationErrorEvent errComp = new CompilationErrorEvent(user, assignment, timeFormat, error, message, file, line);
 				events.add(errComp);

-				success = false;
+				errors++;
 			}

 		}

 		// now record the fact that a compilation occurred
-		CompilationEvent ce = new CompilationEvent(user, assignment, timeFormat, success);
-		mgr.insertEvent(ce);
+		CompilationEvent ce = new CompilationEvent(user, assignment, timeFormat, errors);
+		new CompilationEventManager().insertCompilationEvent(ce);
+		if (Logger.isLogInfo()) Logger.logInfo("Inserted " + ce);

 	}

@@ -249,12 +259,13 @@ public class XMLLoader {
 	}


-	/* Only use this as a standalone method. It currently reads all files from a directory. */
+	/* Only use this as a standalone method. */
 	public static void main(String[] args)
 	{
-		/*
+
 		// this is for reading one file at a time
-		String filename = null;
+		String filename = "..\\inbox\\_mja2128-PrimeNumber.java-02-12-02-03-00.xml";
+		/*
 		if (args.length == 0)
 		{
 			System.out.println("Please specify an XML file to read.");
@@ -265,6 +276,25 @@ public class XMLLoader {
 			filename = args[0];
 		 */

+		try
+		{
+			//create an instance
+			XMLLoader loader = new XMLLoader();
+
+			if (filename.contains(".xml"))
+			{
+				System.out.println("Reading " + filename);
+				loader.readAndLoad(filename);
+			}
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+
+		/*
+
+
 		// to read an entire directory
 		String dirname = null;
 		if (args.length == 0)
@@ -306,6 +336,7 @@ public class XMLLoader {
 		{
 			e.printStackTrace();
 		}
+		*/
 	}

 }
diff --git a/retina/db/XmlServer.java b/retina/db/XmlServer.java
index 9474732..b66a226 100644
--- a/retina/db/XmlServer.java
+++ b/retina/db/XmlServer.java
@@ -10,6 +10,9 @@ import java.io.*;
 import java.net.*;
 import java.util.Scanner;

+import retina.im.MessageSender;
+import retina.common.Logger;
+

 public class XmlServer extends Thread
 {
@@ -116,6 +119,8 @@ public class XmlServer extends Thread
     	    // the input stream for reading from the network
     	    Scanner in = null;

+    	    String fileName = null;
+
     	    try
     		{
     			//System.out.println("Connection established");
@@ -124,7 +129,8 @@ public class XmlServer extends Thread
     			in = new Scanner(socket.getInputStream());

     			// the name of the file should be on the first line
-    			String fileName = "_" + in.nextLine();
+    			// TODO: make this configurable
+    			fileName = "D:/Retina/inbox/_" + in.nextLine();
     			//System.out.println("File is " + fileName);

     			// create the File object
@@ -164,6 +170,7 @@ public class XmlServer extends Thread
     		catch (Exception e)
     		{
     			e.printStackTrace();
+    			if (Logger.isLogError()) Logger.logError("Error loading " + fileName + ": " + e.toString());
     		}
     		finally
     		{
diff --git a/retina/ui/AssignmentPane.java b/retina/ui/AssignmentPane.java
new file mode 100644
index 0000000..6d878ad
--- /dev/null
+++ b/retina/ui/AssignmentPane.java
@@ -0,0 +1,774 @@
+package retina.ui;
+
+/**
+ * This is the pane that is used for the "Browse" feature. A user selects a student and an assignment,
+ * and all compilation errors are listed.
+ */
+
+/*
+ * BrowsePane.java
+ */
+
+
+
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import java.awt.event.*;
+
+import javax.swing.event.ListSelectionEvent;
+
+import javax.swing.event.ListSelectionListener;
+
+import javax.swing.table.TableColumn;
+
+import graph.*;
+
+import retina.common.CompilationErrorEvent;
+import retina.common.CompilationEvent;
+import retina.common.OccurrenceMap;
+import retina.db.CompilationErrorEventManager;
+
+
+
+/**
+ * @author sh2503
+ *
+ */
+public class AssignmentPane extends javax.swing.JPanel {
+
+
+
+    /*Variable Declaration*/
+
+    private javax.swing.JComboBox assignmentCombo;
+
+    private javax.swing.JComboBox statCombo;
+
+    private javax.swing.JLabel assignmentLabel;
+
+    private javax.swing.JButton backButton;
+
+    private javax.swing.JLayeredPane centerPane;
+
+    private javax.swing.JTable commonErrorsTable;
+
+    private javax.swing.JLayeredPane optionsPane;
+
+    private javax.swing.JLayeredPane statsPane;
+
+    private javax.swing.JLayeredPane generalStatsPane;
+
+    private javax.swing.JList studentList;
+
+    private javax.swing.JScrollPane commonErrorsScrollPane;
+
+    private javax.swing.JScrollPane listScrollPane;
+
+    private javax.swing.JLabel selectedStudentLabel;
+
+    private javax.swing.JLabel selectedStatLabel;
+
+    private javax.swing.JLabel studentLabelData;
+
+    private javax.swing.JLabel titleLabel;
+
+    private javax.swing.JLabel topErrorsLabel;
+
+    private javax.swing.JLabel totalErrorsLabel;
+
+    private javax.swing.JLabel totalErrorsLabelData;
+
+    private javax.swing.JLabel totalCompilationsLabel;
+
+    private javax.swing.JLabel totalCompilationsLabelData;
+
+    private javax.swing.JButton viewButton;
+
+    private ListSelectionModel listSelectionModel;
+
+    private String selectedID, selectedAssignment;
+
+    private int selectedStat;
+
+    private CompilationErrorEvent compileError;
+
+    private UserInterfaceManager uimanager;
+
+    private OccurrenceMap commonErrorsMap, compileTimesMap, errorTimesMap;
+
+    private CompilationErrorEventManager errorEventManager;
+
+    private DateConverter dateConverter;
+
+
+    /** Creates new form BrowsePane */
+
+    public AssignmentPane(UserInterfaceManager ui) {
+
+        uimanager = ui;
+
+        initComponents();
+
+    }
+
+
+
+    private void initComponents() {
+
+        errorEventManager = new CompilationErrorEventManager();
+
+        dateConverter = new DateConverter();
+
+        centerPane = new javax.swing.JLayeredPane();
+
+        titleLabel = new javax.swing.JLabel();
+
+        topErrorsLabel = new javax.swing.JLabel();
+
+        optionsPane = new javax.swing.JLayeredPane();
+
+        assignmentCombo = new javax.swing.JComboBox();
+
+        statCombo = new javax.swing.JComboBox();
+
+        assignmentLabel = new javax.swing.JLabel();
+
+        selectedStudentLabel = new javax.swing.JLabel();
+
+        studentLabelData = new javax.swing.JLabel();
+
+        selectedStatLabel = new javax.swing.JLabel();
+
+        viewButton = new javax.swing.JButton();
+
+        statsPane = new javax.swing.JLayeredPane();
+
+        generalStatsPane = new javax.swing.JLayeredPane();
+
+        totalErrorsLabel = new javax.swing.JLabel();
+
+        totalErrorsLabelData = new javax.swing.JLabel();
+
+        totalCompilationsLabel = new javax.swing.JLabel();
+
+        totalCompilationsLabelData = new javax.swing.JLabel();
+
+        backButton = new javax.swing.JButton();
+
+        commonErrorsScrollPane = new javax.swing.JScrollPane();
+
+        commonErrorsTable = new javax.swing.JTable();
+
+        listScrollPane = new javax.swing.JScrollPane();
+
+        studentList = new javax.swing.JList();
+
+        /*All students, all assignments*/
+
+        selectedID = "All";
+
+        selectedAssignment = "1";
+
+        selectedStat = 0;
+
+        setBounds(new java.awt.Rectangle(0, 0, 1010, 740));
+
+        setSize(1010, 740);
+
+        setLocation(0, 0);
+
+        setBackground(new java.awt.Color(153, 204, 255));
+
+        setName("AssignmentPane");
+
+        commonErrorsTable.setVisible(false);
+
+        statsPane.setVisible(false);
+
+        titleLabel.setFont(new java.awt.Font("Verdana", 1, 24));
+
+        titleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+
+        titleLabel.setLabelFor(centerPane);
+
+        titleLabel.setText("ASSIGNMENT STATISTICS");
+
+        titleLabel.setBounds(170, 0, 400, 50);
+
+        centerPane.add(titleLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        optionsPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        assignmentCombo.setModel(new javax.swing.DefaultComboBoxModel(errorEventManager.getAssignments()));
+
+        assignmentCombo.setBounds(170, 18, 140, 20);
+
+        optionsPane.add(assignmentCombo, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        assignmentCombo.getAccessibleContext().setAccessibleParent(optionsPane);
+
+        assignmentLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+
+        assignmentLabel.setLabelFor(optionsPane);
+
+        assignmentLabel.setText("Select an Assignment:");
+
+        assignmentLabel.setBounds(10, 20, 170, 16);
+
+        optionsPane.add(assignmentLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        assignmentLabel.getAccessibleContext().setAccessibleParent(optionsPane);
+
+        selectedStudentLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+
+        selectedStudentLabel.setLabelFor(optionsPane);
+
+        selectedStudentLabel.setText("Selected Student:");
+
+        selectedStudentLabel.setBounds(330, 20, 170, 16);
+
+        optionsPane.add(selectedStudentLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        studentLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
+
+        studentLabelData.setLabelFor(optionsPane);
+
+        studentLabelData.setMaximumSize(new java.awt.Dimension(117, 16));
+
+        studentLabelData.setBounds(450, 20, 170, 16);
+
+        optionsPane.add(studentLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        selectedStatLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+
+        selectedStatLabel.setLabelFor(optionsPane);
+
+        selectedStatLabel.setText("Select a Statistic:");
+
+        selectedStatLabel.setBounds(120, 53, 140, 20);
+
+        optionsPane.add(selectedStatLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        statCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Top 10 Most Common Errors(Entire Class)", "Graph-Errors vs. Time", "Graph-Class # of Compilations vs. Time", "Graph-Class # of Errors vs. Times" }));
+
+        statCombo.setBounds(255, 53, 340, 20);
+
+        optionsPane.add(statCombo, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        optionsPane.setBounds(70, 50, 600, 80);
+
+        centerPane.add(optionsPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        viewButton.setFont(new java.awt.Font("Verdana", 1, 11));
+
+        viewButton.setText("View Data");
+
+        viewButton.setActionCommand("view");
+
+        viewButton.setBounds(320, 140, 100, 23);
+
+        centerPane.add(viewButton, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        statsPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        statsPane.setBounds(20, 170, 730, 470);
+        centerPane.add(statsPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        generalStatsPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());
+
+        totalCompilationsLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+        totalCompilationsLabel.setLabelFor(optionsPane);
+        totalCompilationsLabel.setText("Total Number of Compilations:");
+        totalCompilationsLabel.setBounds(20, 10, 200, 20);
+        generalStatsPane.add(totalCompilationsLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        totalCompilationsLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
+        totalCompilationsLabelData.setLabelFor(optionsPane);
+        totalCompilationsLabelData.setText("");
+        totalCompilationsLabelData.setBounds(240, 10, 200, 20);
+        generalStatsPane.add(totalCompilationsLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        totalErrorsLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+        totalErrorsLabel.setLabelFor(optionsPane);
+        totalErrorsLabel.setText("Total Number of Errors:");
+        totalErrorsLabel.setBounds(20, 40, 170, 20);
+        generalStatsPane.add(totalErrorsLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        totalErrorsLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
+        totalErrorsLabelData.setLabelFor(optionsPane);
+        totalErrorsLabelData.setBounds(190, 40, 520, 20);
+        generalStatsPane.add(totalErrorsLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        generalStatsPane.setBounds(20, 640, 730, 70);
+        centerPane.add(generalStatsPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        backButton.setFont(new java.awt.Font("Verdana", 1, 11));
+        backButton.setText("Back to Main");
+        backButton.setActionCommand("back");
+        backButton.setBounds(640, 10, 120, 30);
+        centerPane.add(backButton, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addComponent(listScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 226, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(centerPane, javax.swing.GroupLayout.DEFAULT_SIZE, 768, Short.MAX_VALUE))
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addComponent(centerPane, javax.swing.GroupLayout.DEFAULT_SIZE, 700, Short.MAX_VALUE)
+            .addComponent(listScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 700, Short.MAX_VALUE)
+        );
+
+       /*Create JList; Data: User IDs retrieved with DatabaseManager method*/
+
+        studentList.setModel(new javax.swing.AbstractListModel() {
+
+            String[] strings = errorEventManager.getStudents();
+
+            public int getSize() { return strings.length; }
+
+            public Object getElementAt(int i) { return strings[i]; }
+
+        });
+
+        studentList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+
+        listScrollPane.setViewportView(studentList);
+
+
+
+        /*ADD A LISTENER TO THE LIST*/
+
+        listSelectionModel = studentList.getSelectionModel();
+
+        listSelectionModel.addListSelectionListener(new ListSelectionListener()
+
+                {
+
+                    public void valueChanged(ListSelectionEvent e){
+
+                            handleValueChanged(e);
+
+                    }
+
+                }
+
+              );
+
+        assignmentCombo.addActionListener(new java.awt.event.ActionListener() {
+
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+
+                handleAssignmentActionPerformed(evt);
+
+            }
+
+        });
+
+        statCombo.addActionListener(new java.awt.event.ActionListener() {
+
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+
+                handleStatActionPerformed(evt);
+
+            }
+
+        });
+
+
+        viewButton.addActionListener(new ActionListener()
+        {
+
+                public void actionPerformed(ActionEvent evt){
+
+                        handleViewButtonActionPerformed(evt);
+
+                }
+
+            }
+
+           );
+
+        backButton.addActionListener(new ActionListener()
+        {
+
+                public void actionPerformed(ActionEvent evt){
+
+                        handleBackButtonActionPerformed(evt);
+
+                }
+
+            }
+
+           );
+
+
+    }
+
+
+
+    /*Method to handle ListSelectionEvent for the Jlist
+
+     *Store the ID selected by user in variable selectedID
+
+     */
+
+    public void handleValueChanged(ListSelectionEvent e) {
+
+        int id_index;
+
+        if (e.getValueIsAdjusting() == false) {
+
+            id_index = studentList.getSelectedIndex();
+
+            if (id_index == -1) {
+
+            }
+
+            else {
+
+                selectedID = (String) studentList.getSelectedValue();
+                studentLabelData.setText(selectedID);
+
+            }
+
+        }
+
+    }
+
+
+
+   /*Method to handle event for JComboBox
+
+    *Store the assignment selected in variable selectedAssignment
+
+    */
+
+    private void handleAssignmentActionPerformed(ActionEvent evt){
+
+         JComboBox cb = (JComboBox) evt.getSource();
+
+         selectedAssignment = (String)cb.getSelectedItem();
+
+    }
+
+
+    /*Method to handle event for JComboBox
+
+     *Store the statistic selected in variable selectedStat
+
+     */
+
+     private void handleStatActionPerformed(ActionEvent evt){
+
+          JComboBox cb = (JComboBox) evt.getSource();
+
+          selectedStat = (int)cb.getSelectedIndex();
+
+     }
+
+
+    /*Unnecessary accessor methods*/
+
+    private String getSelectedAssignment(){
+
+        return selectedAssignment;
+
+    }
+
+    private int getSelectedStat(){
+
+    	return selectedStat;
+
+    }
+
+
+
+    private String getSelectedID(){
+
+        return selectedID;
+
+    }
+
+
+
+    /*Handle viewButton clicked event
+
+     *updateTable method selectedID from Jlist and selectAssignment from JComboBox*/
+
+    public void handleViewButtonActionPerformed(ActionEvent evt){
+
+        String id = getSelectedID();
+
+        String assignment = getSelectedAssignment();
+
+        int stat = getSelectedStat();
+
+        if("view".equals(evt.getActionCommand())){
+
+            viewButton.setEnabled(true);
+
+            updateData(id, assignment, stat);
+
+        }
+
+        else{
+
+            viewButton.setEnabled(false);
+
+        }
+
+    }
+
+    public void handleBackButtonActionPerformed(ActionEvent evt){
+
+        if("back".equals(evt.getActionCommand())){
+
+            backButton.setEnabled(true);
+
+            studentLabelData.setText("");
+
+            totalCompilationsLabelData.setText("");
+
+            totalErrorsLabelData.setText("");
+
+            selectedID = "All";
+
+            selectedAssignment = "1";
+
+            statsPane.removeAll();
+
+            statsPane.setVisible(false);
+
+            uimanager.showMainPane();
+
+        }
+
+        else{
+
+            viewButton.setEnabled(false);
+
+        }
+
+    }
+
+
+    /*updateTable method draws a JTable based on user id and assignment selections
+
+     *from the user by constructing new instance of DBTableModel
+
+     *@param String id User ID
+
+     *@param String assignment Assignment Number
+
+     */
+
+    private void updateData(String id, String assignment, int stat){
+
+    	statsPane.removeAll();
+        totalCompilationsLabelData.setText("");
+        totalErrorsLabelData.setText("");
+
+    	//Selected Stat: 0. Top 10 Most Common Errors
+        if(stat == 0){
+        	//Create a JTable to list top 10 most common errors
+        	final int mostErrors = 10;
+        	try{
+        		commonErrorsMap = errorEventManager.getMostCommonCompilationErrors(assignment, mostErrors);
+        	}
+    		catch(Exception e)
+    		{
+    			e.printStackTrace();
+    		}
+    		if(commonErrorsMap == null){
+    			 JOptionPane.showMessageDialog(this, "No data available for selected assignment, student, or statistic");
+
+    		}
+    		else{
+    			String[] headers = new String[3];
+    			headers[0] = "";
+    			headers[1] = "Error";
+    			headers[2] = "Frequency";
+    			Object[][] data = new Object[commonErrorsMap.size()][3];
+    			int col = 0, row = 0;
+    			for(row = 0; row < commonErrorsMap.size(); row++)
+    			{
+    				data[row][col++] = (row)+1;
+    				data[row][col++] = commonErrorsMap.keys[row];
+    				data[row][col] = commonErrorsMap.occurrences[row];
+    				col = 0;
+    			}
+    		    topErrorsLabel.setText("Top 10 Most Common Errors");
+    		    topErrorsLabel.setBounds(300, 30, 300, 100);
+    		    statsPane.add(topErrorsLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+    			commonErrorsTable.setModel(new javax.swing.table.DefaultTableModel(data, headers));
+    			commonErrorsScrollPane.setViewportView(commonErrorsTable);
+    			TableColumn column = commonErrorsTable.getColumnModel().getColumn(0);
+    			column.setMaxWidth(30);
+    			commonErrorsScrollPane.setBounds(90, 120, 560, 190);
+    			statsPane.add(commonErrorsScrollPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+    			statsPane.setVisible(true);
+    			commonErrorsTable.setVisible(true);
+    		}
+        }
+        else if (stat == 1){
+        	//Stat 1: Graph of Number of Errors over Time
+        	if(id.equals("All")){
+        		JOptionPane.showMessageDialog(this, "Please select a student ID");
+
+        	}
+        	else{
+        		CompilationEvent[] events = errorEventManager.getCompilationEvents(id, assignment);
+        		int length = events.length;
+        		int i = 0;
+        		int totalerrors = 0;
+        		String[] dates = new String[length];
+        		int[] errorsnum = new int[length];
+
+        		//Check to see if student made any errors before plotting graph
+        		if(length != errorEventManager.getNumberOfSuccessfulCompilations(id, assignment))
+        		{
+        			for(i = 0; i < length; i++)
+        			{
+        				dates[i] = events[i].getTime();
+        				errorsnum[i] = events[i].getErrors();
+        				totalerrors += errorsnum[i];
+        			}
+
+
+        		//Create basic integer array for x-axis
+        			int[] xaxis = new int[length];
+        			for(i = 0; i < length; i++)
+        			{
+        				xaxis[i] = i*5;
+        			}
+
+        			StatGraph etgraph = new StatGraph(xaxis, errorsnum, length);
+        			etgraph.setXTitle("Time");
+        			etgraph.setYTitle("Number of Errors per Compilation");
+        			Graph2D graph = etgraph.getGraph();
+        			graph.setBounds(10, 10, 710, 455);
+        			statsPane.add(graph, javax.swing.JLayeredPane.DEFAULT_LAYER);
+        			statsPane.setVisible(true);
+
+        			//Update general stats: Total number of compilations, total number of errors
+
+        			 String totalCompilations = Integer.toString(errorEventManager.getTotalNumberOfCompilations(id, assignment));
+        	         String successfulCompilations = Integer.toString(errorEventManager.getNumberOfSuccessfulCompilations(id, assignment));
+        	         totalCompilationsLabelData.setText(totalCompilations + "  (" + successfulCompilations + " successful)");
+        	         totalErrorsLabelData.setText(Integer.toString(totalerrors));
+
+        		}
+        		else if(length == 0)
+        		{
+        			JOptionPane.showMessageDialog(this, "No data available for this student on this assignment");
+
+        		}
+        		else
+        		{
+        			JOptionPane.showMessageDialog(this, "No errors were made by this student on this assignment");
+
+        		}
+           	}
+        }
+        else if(stat == 2){
+        //Graph the number of compilations over time for the whole class
+        	try{
+        		compileTimesMap = errorEventManager.getCompilationTimes(assignment);
+            }
+            catch(Exception e)
+            {
+            	e.printStackTrace();
+            }
+            if(compileTimesMap == null){
+            	JOptionPane.showMessageDialog(this, "No data available for selected assignment");
+
+            }
+            else{
+
+            	int col = 0, row = 0;
+            	//Create basic integer array for x-axis
+            	int length = compileTimesMap.size();
+    			int[] xaxis = new int[length];
+
+    			String[] date_strings = compileTimesMap.keys;
+
+    			for(int i = 0; i < length; i++)
+    			{
+    				xaxis[i] = dateConverter.dateToInt(date_strings[i]);
+
+    			}
+
+    		    StatGraph ctgraph = new StatGraph(xaxis, compileTimesMap.occurrences, length);
+    		//	ctgraph.setXLabels(dates);
+    			ctgraph.setXTitle("Date & Time");
+    			ctgraph.setYTitle("Number of Compilations");
+
+    			Graph2D graph = ctgraph.getGraph();
+    			graph.setBounds(10, 10, 710, 455);
+    			statsPane.add(graph, javax.swing.JLayeredPane.DEFAULT_LAYER);
+    			statsPane.setVisible(true);
+
+            }
+        }
+        else if(stat == 3){
+            //Graph the number of compilations over time for the whole class
+            	try{
+            		errorTimesMap = errorEventManager.getCompilationErrorTimes(assignment);
+                }
+                catch(Exception e)
+                {
+                	e.printStackTrace();
+                }
+                if(errorTimesMap == null){
+                	JOptionPane.showMessageDialog(this, "No data available for selected assignment");
+
+                }
+                else{
+
+                	int col = 0, row = 0;
+                	//Create basic integer array for x-axis
+                	int length = errorTimesMap.size();
+        			int[] xaxis = new int[length];
+
+        			String[] date_strings = errorTimesMap.keys;
+
+        			for(int i = 0; i < length; i++)
+        			{
+        				xaxis[i] = dateConverter.dateToInt(date_strings[i]);
+        			}
+
+
+        			StatGraph errorTimesGraph = new StatGraph(xaxis, errorTimesMap.occurrences, length);
+        	//		errorTimesGraph.setXLabels(dates);
+        			errorTimesGraph.setXTitle("Date & Time");
+        			errorTimesGraph.setYTitle("Number of Errors");
+
+        			Graph2D graph = errorTimesGraph.getGraph();
+        			graph.setBounds(10, 10, 710, 455);
+        			statsPane.add(graph, javax.swing.JLayeredPane.DEFAULT_LAYER);
+        			statsPane.setVisible(true);
+
+                }
+            }
+
+
+    }
+
+    public static void main(String args[]) {
+        java.awt.EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                new AssignmentPane(new UserInterfaceManager()).setVisible(true);
+            }
+        });
+    }
+
+}
+
diff --git a/retina/ui/BrowsePane.java b/retina/ui/BrowsePane.java
index b87801a..4aa8de6 100644
--- a/retina/ui/BrowsePane.java
+++ b/retina/ui/BrowsePane.java
@@ -25,10 +25,14 @@ import javax.swing.event.ListSelectionListener;
 import javax.swing.table.TableColumn;

 import retina.common.CompilationErrorEvent;
-import retina.db.DatabaseManager;
+import retina.db.CompilationErrorEventManager;



+/**
+ * @author sh2503
+ *
+ */
 public class BrowsePane extends javax.swing.JPanel {


@@ -43,6 +47,8 @@ public class BrowsePane extends javax.swing.JPanel {

     private javax.swing.JLayeredPane centerPane;

+    private javax.swing.JLayeredPane statsPane;
+
     private javax.swing.JLabel commonErrorLabel;

     private javax.swing.JLabel commonErrorLabelData;
@@ -55,8 +61,6 @@ public class BrowsePane extends javax.swing.JPanel {

     private javax.swing.JLabel selectedStudentLabel;

-    private javax.swing.JLayeredPane statsPane;
-
     private javax.swing.JLabel studentLabelData;

     private javax.swing.JList studentList;
@@ -70,7 +74,11 @@ public class BrowsePane extends javax.swing.JPanel {
     private javax.swing.JLabel totalErrorsLabelData;

     private javax.swing.JLabel totalCompilationsLabel;
-
+
+    private javax.swing.JLabel timeSpentLabel;
+
+    private javax.swing.JLabel timeSpentLabelData;
+
     private javax.swing.JLabel totalCompilationsLabelData;

     private javax.swing.JButton viewButton;
@@ -83,9 +91,9 @@ public class BrowsePane extends javax.swing.JPanel {

     private CompilationErrorEvent compileError;

-    private DatabaseManager dbmanager;
-
     private UserInterfaceManager uimanager;
+
+    private CompilationErrorEventManager errorEventManager;


     /** Creates new form BrowsePane */
@@ -102,7 +110,7 @@ public class BrowsePane extends javax.swing.JPanel {

     private void initComponents() {

-        dbmanager = new DatabaseManager();
+        errorEventManager = new CompilationErrorEventManager();

         centerPane = new javax.swing.JLayeredPane();

@@ -133,6 +141,10 @@ public class BrowsePane extends javax.swing.JPanel {
         totalCompilationsLabel = new javax.swing.JLabel();

         totalCompilationsLabelData = new javax.swing.JLabel();
+
+        timeSpentLabel = new javax.swing.JLabel();
+
+        timeSpentLabelData = new javax.swing.JLabel();

         backButton = new javax.swing.JButton();

@@ -149,6 +161,8 @@ public class BrowsePane extends javax.swing.JPanel {
         selectedID = "All";

         selectedAssignment = "1";
+
+        tableScrollPane.setVisible(false);

         setBounds(new java.awt.Rectangle(0, 0, 1010, 740));

@@ -174,7 +188,7 @@ public class BrowsePane extends javax.swing.JPanel {

         optionsPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());

-        assignmentCombo.setModel(new javax.swing.DefaultComboBoxModel(dbmanager.getAssignments()));
+        assignmentCombo.setModel(new javax.swing.DefaultComboBoxModel(errorEventManager.getAssignments()));

         assignmentCombo.setBounds(170, 20, 140, 20);
         optionsPane.add(assignmentCombo, javax.swing.JLayeredPane.DEFAULT_LAYER);
@@ -211,40 +225,52 @@ public class BrowsePane extends javax.swing.JPanel {
         statsPane.setBorder(javax.swing.BorderFactory.createEtchedBorder());

         totalCompilationsLabel.setFont(new java.awt.Font("Verdana", 1, 12));
-        totalCompilationsLabel.setLabelFor(optionsPane);
+        totalCompilationsLabel.setLabelFor(statsPane);
         totalCompilationsLabel.setText("Total Number of Compilations:");
-        totalCompilationsLabel.setBounds(20, 10, 200, 20);
+        totalCompilationsLabel.setBounds(10, 10, 210, 20);
         statsPane.add(totalCompilationsLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

         totalCompilationsLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
-        totalCompilationsLabelData.setLabelFor(optionsPane);
-        totalCompilationsLabelData.setText("0");
-        totalCompilationsLabelData.setBounds(240, 10, 200, 20);
+        totalCompilationsLabelData.setLabelFor(statsPane);
+        totalCompilationsLabelData.setText("");
+        totalCompilationsLabelData.setBounds(240, 10, 140, 20);
         statsPane.add(totalCompilationsLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);
-
+
         totalErrorsLabel.setFont(new java.awt.Font("Verdana", 1, 12));
-        totalErrorsLabel.setLabelFor(optionsPane);
+        totalErrorsLabel.setLabelFor(statsPane);
         totalErrorsLabel.setText("Total Number of Errors:");
-        totalErrorsLabel.setBounds(20, 40, 170, 20);
+        totalErrorsLabel.setBounds(10, 40, 200, 20);
         statsPane.add(totalErrorsLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

         totalErrorsLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
-        totalErrorsLabelData.setLabelFor(optionsPane);
-        totalErrorsLabelData.setBounds(190, 40, 520, 20);
+        totalErrorsLabelData.setLabelFor(statsPane);
+        totalErrorsLabelData.setBounds(190, 40, 70, 20);
         statsPane.add(totalErrorsLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);

+        timeSpentLabel.setFont(new java.awt.Font("Verdana", 1, 12));
+        timeSpentLabel.setLabelFor(statsPane);
+        timeSpentLabel.setText("Time spent on assignment:");
+        timeSpentLabel.setBounds(250, 40, 190, 20);
+        statsPane.add(timeSpentLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        timeSpentLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
+        timeSpentLabelData.setLabelFor(statsPane);
+        timeSpentLabelData.setText("");
+        timeSpentLabelData.setBounds(440, 40, 250, 20);
+        statsPane.add(timeSpentLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
         commonErrorLabel.setFont(new java.awt.Font("Verdana", 1, 12));
-        commonErrorLabel.setLabelFor(optionsPane);
+        commonErrorLabel.setLabelFor(statsPane);
         commonErrorLabel.setText("Most Common Error:");
-        commonErrorLabel.setBounds(20, 60, 170, 20);
+        commonErrorLabel.setBounds(10, 70, 170, 20);
         statsPane.add(commonErrorLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

         commonErrorLabelData.setFont(new java.awt.Font("Verdana", 1, 12));
-        commonErrorLabelData.setLabelFor(optionsPane);
-        commonErrorLabelData.setBounds(190, 60, 550, 20);
+        commonErrorLabelData.setLabelFor(statsPane);
+        commonErrorLabelData.setBounds(190, 70, 550, 20);
         statsPane.add(commonErrorLabelData, javax.swing.JLayeredPane.DEFAULT_LAYER);

-        statsPane.setBounds(20, 610, 730, 80);
+        statsPane.setBounds(20, 600, 730, 100);
         centerPane.add(statsPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

         backButton.setFont(new java.awt.Font("Verdana", 1, 11));
@@ -278,7 +304,7 @@ public class BrowsePane extends javax.swing.JPanel {

         studentList.setModel(new javax.swing.AbstractListModel() {

-            String[] strings = dbmanager.getStudents();
+            String[] strings = errorEventManager.getStudents();

             public int getSize() { return strings.length; }

@@ -393,7 +419,7 @@ public class BrowsePane extends javax.swing.JPanel {
          JComboBox cb = (JComboBox) evt.getSource();

          selectedAssignment = (String)cb.getSelectedItem();
-
+
     }


@@ -430,7 +456,16 @@ public class BrowsePane extends javax.swing.JPanel {

             viewButton.setEnabled(true);

-            updateData(id, assignment);
+            if(id.equals("All"))
+            {
+
+                JOptionPane.showMessageDialog(this, "Please select a student ID");
+            }
+            else
+            {
+
+            	updateData(id, assignment);
+            }

         }

@@ -447,6 +482,24 @@ public class BrowsePane extends javax.swing.JPanel {
         if("back".equals(evt.getActionCommand())){

             backButton.setEnabled(true);
+
+            studentLabelData.setText("");
+
+            selectedID = "All";
+
+            selectedAssignment = "1";
+
+    	    commonErrorLabelData.setText("");
+
+    	    totalErrorsLabelData.setText("");
+
+    	    totalCompilationsLabelData.setText("");
+
+    	    timeSpentLabelData.setText("");
+
+    	    assignmentCombo.setSelectedIndex(0);
+
+            centerPane.remove(tableScrollPane);

             uimanager.showMainPane();

@@ -473,30 +526,30 @@ public class BrowsePane extends javax.swing.JPanel {

     private void updateData(String id, String assignment){

-        // dataTable.setModel(new DBTableModel(id, assignment));

         try {
-
-            CompilationErrorEvent[] errors = dbmanager.getCompilationErrorEvents(id, assignment);
+            CompilationErrorEvent[] errors = errorEventManager.getCompilationErrorEvents(id, assignment);
             if(errors == null)
             {
                 //dataTable.setModel(new DBTableModel(dbmanager.getCompilationErrorEvents(id, assignment)));
-                JOptionPane.showMessageDialog(this, "No data available for selected student and assignment.");
+                JOptionPane.showMessageDialog(this, "No data available for selected student and assignment");
                 compileError = null;
             }
             else
             {
-                compileError = dbmanager.getMostCommonCompilationError(id, assignment);
+                compileError = errorEventManager.getMostCommonCompilationError(id, assignment);
             }

             dbtable = new DBTableModel(errors);
             dataTable.setModel(dbtable);
+            tableScrollPane.setVisible(true);

             if(compileError != null)
             {

                 commonErrorLabelData.setText(compileError.getError());
                 totalErrorsLabelData.setText(Integer.toString(dbtable.getRowCount()));
+
             }
             else
             {
@@ -505,9 +558,18 @@ public class BrowsePane extends javax.swing.JPanel {
                 totalErrorsLabelData.setText("");
             }

-            String totalCompilations = Integer.toString(dbmanager.getTotalNumberOfCompilations(id, assignment));
-            String successfulCompilations = Integer.toString(dbmanager.getNumberOfSuccessfulCompilations(id, assignment));
+            String totalCompilations = Integer.toString(errorEventManager.getTotalNumberOfCompilations(id, assignment));
+            String successfulCompilations = Integer.toString(errorEventManager.getNumberOfSuccessfulCompilations(id, assignment));
             totalCompilationsLabelData.setText(totalCompilations + "  (" + successfulCompilations + " successful)");
+
+            DateConverter dc = new DateConverter();
+            long[] workTime = dc.computeWorkTime(errorEventManager.getCompilationEvents(id, assignment));
+            if(workTime[0] == 0){
+            	timeSpentLabelData.setText("Less Than 1 Hour");
+            }
+            else{
+            	timeSpentLabelData.setText("Approx. " + (int)workTime[0] + " hrs. and " + (int)workTime[1] + " mins.");
+            }


         } catch (Exception ex) {
diff --git a/retina/ui/DBTableModel.java b/retina/ui/DBTableModel.java
index 6295ca4..99d9928 100644
--- a/retina/ui/DBTableModel.java
+++ b/retina/ui/DBTableModel.java
@@ -11,9 +11,9 @@ import retina.common.Event;

 public class DBTableModel extends AbstractTableModel
 {
-    private String[] columnNames = {"Error", "Message", "Time"};
+    private String[] columnNames = {"Error", "Message", "Time", "File", "Line"};
     private Object[][] data;
-    final int COLUMN_MAX = 3;
+    final int COLUMN_MAX = 5;

     /*CONSTRUCTOR METHODS*/
     /**
@@ -54,6 +54,8 @@ public class DBTableModel extends AbstractTableModel
             data[row][col++] = ce[row].getError();
             data[row][col++] = ce[row].getMessage();
             data[row][col++] = ce[row].getTime();
+            data[row][col++] = ce[row].getFile();
+            data[row][col++] = ce[row].getLine();
             // data[row][col++] = ce[row].getUser();
             col = 0;

diff --git a/retina/ui/DateConverter.java b/retina/ui/DateConverter.java
new file mode 100644
index 0000000..9f5668b
--- /dev/null
+++ b/retina/ui/DateConverter.java
@@ -0,0 +1,77 @@
+package retina.ui;
+
+import java.util.*;
+import java.text.*;
+
+import retina.common.CompilationEvent;
+
+public class DateConverter {
+
+	private final int timeCap = 3600;		 //units = "seconds"
+	private long workTime = 0;
+	private DateFormat formatter;
+
+	public int dateToInt(String date){
+		StringTokenizer st = new StringTokenizer(date, "\\d-\\d \\d:\\d");
+	    String newdate = "";
+	    while(st.hasMoreTokens()){
+	        String temp = st.nextToken();
+	       if(temp.length() < 2){
+	        	temp = "0" + temp;
+	        }
+	        newdate += temp;
+
+
+	    }
+
+	    return Integer.parseInt(newdate);
+
+	}
+
+	public long[] computeWorkTime(CompilationEvent[] errorEvents){
+
+		long[] outputWorkTime = new long[3];
+
+		try{
+		formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		int i=0;
+
+		for(int j=i+1; j<errorEvents.length; j++) {
+			String login = errorEvents[i].getTime();
+			String logout = errorEvents[j].getTime();
+	        Date dateLogin = (Date)formatter.parse(login);
+	        Date dateLogout = (Date)formatter.parse(logout);
+
+	        long startVal = dateLogin.getTime();
+	        long endVal = dateLogout.getTime();
+	        long loginSec = (endVal-startVal)/1000;
+	        if (loginSec < timeCap) {
+	        	workTime += loginSec;
+	        }
+
+	        i++;
+		} //end for-loop
+
+		/** This converts the total documented workTime of a student from seconds
+		 *  into hours, minutes, and seconds. It is then printed as a time estimate.
+		 */
+		long hours = (workTime-workTime%3600)/3600;
+        workTime = workTime-(hours*3600);
+        long minutes = (workTime-workTime%60)/60;
+        workTime = workTime-(minutes*60);
+        long seconds = workTime;
+
+     /*   String[] outputWorkTime = new String[3];
+        outputWorkTime[0] = String.valueOf(hours);
+        outputWorkTime[1] = String.valueOf(minutes);
+        outputWorkTime[2] = String.valueOf(seconds); */
+
+        outputWorkTime[0] = hours;
+        outputWorkTime[1] = minutes;
+        outputWorkTime[2] = seconds;
+       	} //end try{}
+		catch (Exception e) { System.out.println("Error: "+e); }
+
+		return outputWorkTime;
+	}
+}
diff --git a/retina/ui/ErrorTimeGraph.java b/retina/ui/ErrorTimeGraph.java
new file mode 100644
index 0000000..9c9b4a5
--- /dev/null
+++ b/retina/ui/ErrorTimeGraph.java
@@ -0,0 +1,75 @@
+package retina.ui;
+
+import java.awt.*;
+import javax.swing.*;
+import java.applet.*;
+import graph.*;
+
+public class ErrorTimeGraph
+{
+
+	Graph2D graph;
+    DataSet data;
+    Axis    xaxis;
+    Axis    yaxis_left;
+    Axis    yaxis_right;
+    double d1[];
+    int np = 100000;
+
+    public ErrorTimeGraph(int[] x, int[] y, int points){
+    	plotGraph(x, y, points);
+    }
+
+    public Graph2D getGraph()
+    {
+    	return graph;
+    }
+
+    public void plotGraph(int[] x, int[] y, int points) {
+      int i;
+      final int MAXPOINTS=points;
+      double d1[] = new double[MAXPOINTS*2];
+
+      graph = new Graph2D();
+      graph.drawzero = false;
+      graph.drawgrid = false;
+      graph.borderTop = 50;
+      graph.borderRight=100;
+
+    /*  setLayout( new BorderLayout() );
+      add("Center", graph); */
+
+      for(i=0; i<MAXPOINTS; i++)
+      {
+    	  d1[i*2]=i; //x values
+    	  d1[i*2+1]=y[i]; //y values
+      }
+
+      data = graph.loadDataSet(d1,MAXPOINTS);
+      data.linestyle = 1;
+      data.linecolor = Color.black;
+      data.marker = 1;
+      data.markerscale = 1;
+      data.markercolor = new Color(0,0,255);
+     // data.legend(200,100,"Y=X, linear");
+     // data.legendColor(Color.black);
+
+      xaxis = graph.createAxis(Axis.BOTTOM);
+      xaxis.attachDataSet(data);
+      xaxis.setTitleText("Time");
+      xaxis.setTitleFont(new Font("Arial",Font.PLAIN,20));
+      xaxis.setLabelFont(new Font("Arial",Font.PLAIN,15));
+/*
+**      Attach the first data set to the Left Axis
+*/
+      yaxis_left = graph.createAxis(Axis.LEFT);
+      yaxis_left.attachDataSet(data);
+      yaxis_left.setTitleText("Number of Errors per CompilationEvent");
+      yaxis_left.setTitleFont(new Font("Arial",Font.PLAIN,20));
+      yaxis_left.setLabelFont(new Font("Arial",Font.PLAIN,15));
+      yaxis_left.setTitleColor(Color.black);
+
+    }
+
+}
+
diff --git a/retina/ui/MainPane.java b/retina/ui/MainPane.java
index c492eb4..9b0c89b 100644
--- a/retina/ui/MainPane.java
+++ b/retina/ui/MainPane.java
@@ -81,14 +81,14 @@ public class MainPane extends javax.swing.JPanel {

         setName("MainPane");

-        buttonGroup = new javax.swing.ButtonGroup();
         centerPane = new javax.swing.JLayeredPane();
-        titleLabel = new javax.swing.JLabel();
         optionsPane = new javax.swing.JLayeredPane();
+        buttonGroup = new javax.swing.ButtonGroup();
         buttonBrowse = new javax.swing.JRadioButton();
         buttonEStat = new javax.swing.JRadioButton();
         buttonAStat = new javax.swing.JRadioButton();
         buttonSStat = new javax.swing.JRadioButton();
+        titleLabel = new javax.swing.JLabel();
         browseDescriptionLabel = new javax.swing.JLabel();
         assignmentDescriptionLabel = new javax.swing.JLabel();
         studentDescriptionLabel = new javax.swing.JLabel();
@@ -103,11 +103,18 @@ public class MainPane extends javax.swing.JPanel {
         setMaximumSize(new java.awt.Dimension(1000, 700));
         setMinimumSize(new java.awt.Dimension(1000, 700));
         setName("BrowsePane");
+
+        projectLabel.setFont(new java.awt.Font("Britannic Bold", 1, 40));
+        projectLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+        projectLabel.setText("RETINA VIEWER");
+        projectLabel.setBounds(315, 10, 380, 70);
+        centerPane.add(projectLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
         titleLabel.setFont(new java.awt.Font("Verdana", 1, 24));
         titleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
         titleLabel.setLabelFor(centerPane);
         titleLabel.setText("MAIN MENU");
-        titleLabel.setBounds(380, 160, 190, 50);
+        titleLabel.setBounds(410, 160, 190, 50);
         centerPane.add(titleLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

         optionsPane.setBackground(new java.awt.Color(152, 204, 255));
@@ -180,13 +187,13 @@ public class MainPane extends javax.swing.JPanel {
         errorDescriptionLabel.setBounds(450, 70, 370, 70);
         optionsPane.add(errorDescriptionLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

-        optionsPane.setBounds(100, 220, 830, 270);
+        optionsPane.setBounds(155, 220, 700, 270);
         centerPane.add(optionsPane, javax.swing.JLayeredPane.DEFAULT_LAYER);

         goButton.setFont(new java.awt.Font("Verdana", 1, 18));
         goButton.setText("Start");
         goButton.setActionCommand("start");
-        goButton.setBounds(410, 530, 130, 50);
+        goButton.setBounds(410, 530, 130, 30);
         centerPane.add(goButton, javax.swing.JLayeredPane.DEFAULT_LAYER);

         classLabel.setFont(new java.awt.Font("Verdana", 1, 30));
@@ -194,12 +201,6 @@ public class MainPane extends javax.swing.JPanel {
         classLabel.setBounds(370, 110, 240, 50);
         centerPane.add(classLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);

-        projectLabel.setFont(new java.awt.Font("Britannic Bold", 1, 40));
-        projectLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        projectLabel.setText("RETINA VIEWER");
-        projectLabel.setBounds(290, 10, 380, 70);
-        centerPane.add(projectLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
-
         buttonGroup.add(buttonBrowse);
         buttonGroup.add(buttonAStat);
         buttonGroup.add(buttonEStat);
@@ -259,18 +260,18 @@ public class MainPane extends javax.swing.JPanel {

         if(selectedButton.equals(browseString)){
              uimanager.showBrowsePane();
-
-
+
         }
         else if (selectedButton.equals(estatString)){


         }
         else if (selectedButton.equals(astatString)){
+        	uimanager.showAssignPane();


         }
-       else if (selectedButton.equals(astatString)){
+       else if (selectedButton.equals(sstatString)){


         }
diff --git a/retina/ui/StatGraph.java b/retina/ui/StatGraph.java
new file mode 100644
index 0000000..2517d37
--- /dev/null
+++ b/retina/ui/StatGraph.java
@@ -0,0 +1,97 @@
+package retina.ui;
+
+import java.awt.*;
+import javax.swing.*;
+import java.applet.*;
+import graph.*;
+
+public class StatGraph
+{
+
+	Graph2D graph;
+    DataSet data;
+    Axis    xaxis;
+    Axis    yaxis_left;
+    Axis    yaxis_right;
+    double d1[];
+    int np = 100000;
+
+    public StatGraph(){
+    }
+
+
+    public StatGraph(int[] x, int[] y, int points){
+    	plotGraph(x, y, points);
+    }
+
+    public Graph2D getGraph()
+    {
+    	return graph;
+    }
+
+    public void setXTitle(String title){
+    	 xaxis.setTitleText(title);
+
+    }
+
+    public void setYTitle(String title){
+    	yaxis_left.setTitleText(title);
+
+   }
+
+    public void setXLabels(String[] label){
+    	xaxis.label_string = label;
+    }
+
+    public void setYLabels(String[] label){
+    	yaxis_left.label_string = label;
+    }
+
+
+    public void plotGraph(int[] x, int[] y, int points) {
+      int i;
+      final int MAXPOINTS=points;
+      double d1[] = new double[MAXPOINTS*2];
+
+      graph = new Graph2D();
+      graph.drawzero = false;
+      graph.drawgrid = false;
+      graph.borderTop = 50;
+      graph.borderRight=100;
+
+    /*  setLayout( new BorderLayout() );
+      add("Center", graph); */
+
+      for(i=0; i<MAXPOINTS; i++)
+      {
+    	  d1[i*2]=i; //x values
+    	  d1[i*2+1]=y[i]; //y values
+      }
+
+      xaxis = graph.createAxis(Axis.BOTTOM);
+      yaxis_left = graph.createAxis(Axis.LEFT);
+
+      data = graph.loadDataSet(d1,MAXPOINTS);
+      data.linestyle = 1;
+      data.linecolor = Color.black;
+      data.marker = 1;
+      data.markerscale = 1;
+      data.markercolor = new Color(0,0,255);
+     // data.legend(200,100,"Y=X, linear");
+     // data.legendColor(Color.black);
+
+      xaxis.attachDataSet(data);
+      xaxis.setTitleFont(new Font("Arial",Font.PLAIN,20));
+      xaxis.setLabelFont(new Font("Arial",Font.PLAIN,15));
+/*
+**      Attach the first data set to the Left Axis
+*/
+      yaxis_left.attachDataSet(data);
+      yaxis_left.setTitleFont(new Font("Arial",Font.PLAIN,20));
+      yaxis_left.setLabelFont(new Font("Arial",Font.PLAIN,15));
+      yaxis_left.setTitleColor(Color.black);
+
+    }
+
+}
+
diff --git a/retina/ui/UserInterfaceManager.java b/retina/ui/UserInterfaceManager.java
index b6fb6c4..0b9d539 100644
--- a/retina/ui/UserInterfaceManager.java
+++ b/retina/ui/UserInterfaceManager.java
@@ -1,51 +1,14 @@
-package retina.ui;
-/**
- * This class contains some helper methods for dealing with UI stuff
- */
-
-import javax.swing.*;
-
-public class UserInterfaceManager extends JFrame
-{
-    private MainPane mainpane;
-    private BrowsePane browsepane;
-    public UserInterfaceManager() {
-        initComponents();
-        showMainPane();
-    }
-
-    private void initComponents(){
-        setSize(1010, 740);
-        setLocation(10, 10);
-        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
-        setBounds(new java.awt.Rectangle(10, 10, 1010, 740 ));
-        setVisible(true);
-        setResizable(true);
-        setTitle("Retina Viewer");
-        mainpane = new MainPane(this);
-        add(mainpane);
-        browsepane = new BrowsePane(this);
-        add(browsepane);
-    }
-
-    public void showMainPane(){
-        browsepane.setVisible(false);
-        mainpane.setVisible(true);
-        validate();
-    }
-
-    public void showBrowsePane(){
-        mainpane.setVisible(false);
-        browsepane.setVisible(true);
-        validate();
-    }
-
-    public static void main(String args[]) {
-        java.awt.EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                new UserInterfaceManager();
-            }
-        });
-    }
-
-}
+package retina.ui;

/**
 * This class contains some helper methods for dealing with UI stuff
 */
+
+import javax.swing.*;
+public class UserInterfaceManager extends JFrame
{
    private MainPane mainpane; 
    private BrowsePane browsepane;
    private AssignmentPane assignpane;

    public UserInterfaceManager() {
        initComponents(); 
        showMainPane();
    }
+
+    private void initComponents(){
        setSize(1010, 740);
        setLocation(10, 10);
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setBounds(new java.awt.Rectangle(120, 10, 1010, 740 ));
        setVisible(true);
        setResizable(true);
        setTitle("RetinaServer Viewer");

        mainpane = new MainPane(this); 
        add(mainpane);

        browsepane = new BrowsePane(this);
        add(browsepane);
        
        assignpane = new AssignmentPane(this);
        add(assignpane);
    }
+    public void showAssignPane(){
    	browsepane.setVisible(false);
    	mainpane.setVisible(false);
    	assignpane.setVisible(true);
    	validate();
    }
+    public void showMainPane(){
        browsepane.setVisible(false);
        assignpane.setVisible(false);
        mainpane.setVisible(true);
        validate(); 
    }
+
+    public void showBrowsePane(){
        mainpane.setVisible(false);
        assignpane.setVisible(false);
        browsepane.setVisible(true);
        validate(); 
    }
+
+    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new UserInterfaceManager(); 
            }
        });
    }
+
+}