001package edu.pdx.cs410J.grader.gradebook.ui; 002 003import edu.pdx.cs410J.ParserException; 004import edu.pdx.cs410J.grader.gradebook.GradeBook; 005import edu.pdx.cs410J.grader.gradebook.Student; 006import edu.pdx.cs410J.grader.gradebook.XmlDumper; 007import edu.pdx.cs410J.grader.gradebook.XmlGradeBookParser; 008 009import javax.swing.*; 010import javax.swing.event.ListSelectionEvent; 011import javax.swing.event.ListSelectionListener; 012import java.awt.*; 013import java.awt.event.ActionEvent; 014import java.awt.event.ActionListener; 015import java.awt.event.WindowAdapter; 016import java.awt.event.WindowEvent; 017import java.io.FileNotFoundException; 018import java.io.IOException; 019import java.util.*; 020 021/** 022 * This panel displays the contents of a grade book. On the left-hand 023 * side of the panel is a list of the students in the class. In the 024 * center of the panel is a tabbed pane that lets you view/edit 025 * information about the class, a student, or a student's grades. 026 */ 027@SuppressWarnings("serial") 028public class GradeBookPanel extends JPanel { 029 static final int BY_ID = 1; 030 static final int BY_NAME = 2; 031 032 private GradeBook book; // The GradeBook being displayed 033 private StudentsModel model; 034 035 // GUI components we care about 036 private JList students; 037 private ClassPanel classPanel; 038 private StudentPanel studentPanel; 039 private GradePanel gradePanel; 040 041 /** 042 * Creates a new <code>GradeBookPanel</code> and lays out all of its 043 * components. 044 */ 045 public GradeBookPanel(JFrame parent) { 046 this.classPanel = new ClassPanel(parent); 047 this.studentPanel = new StudentPanel(); 048 this.gradePanel = new GradePanel(); 049 050 JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP); 051 tabs.addTab("Class", this.classPanel); 052 tabs.addTab("Student", this.studentPanel); 053 tabs.addTab("Grades", this.gradePanel); 054 055 JSplitPane split = new JSplitPane(); 056 split.setRightComponent(tabs); 057 058 JPanel leftPanel = new JPanel(); 059 leftPanel.setLayout(new BorderLayout()); 060 leftPanel.add(new JLabel("Students", JLabel.CENTER), 061 BorderLayout.NORTH); 062 063 this.students = new JList(); 064 this.students.addListSelectionListener(new 065 ListSelectionListener() { 066 public void valueChanged(ListSelectionEvent e) { 067 if (GradeBookPanel.this.model == null) { 068 return; 069 } 070 071 int index = students.getSelectedIndex(); 072 073 if (index >= 0) { 074 String id = model.getIdAt(index); 075 displayStudent(id); 076 077 } else { 078 GradeBookPanel.this.studentPanel.clearContents(); 079 } 080 } 081 }); 082 this.students.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); 083 084 leftPanel.add(new JScrollPane(this.students), BorderLayout.CENTER); 085 JPanel buttons = new JPanel(); 086 buttons.setLayout(new FlowLayout()); 087 ButtonGroup group = new ButtonGroup(); 088 089 JRadioButton idButton = new JRadioButton("By id"); 090 idButton.addActionListener(new ActionListener() { 091 public void actionPerformed(ActionEvent e) { 092 displayStudents(BY_ID); 093 } 094 }); 095 group.add(idButton); 096 buttons.add(idButton); 097 098 JRadioButton nameButton = new JRadioButton("By name"); 099 nameButton.addActionListener(new ActionListener() { 100 public void actionPerformed(ActionEvent e) { 101 displayStudents(BY_NAME); 102 } 103 }); 104 group.add(nameButton); 105 buttons.add(nameButton); 106 leftPanel.add(buttons, BorderLayout.SOUTH); 107 108 split.setLeftComponent(leftPanel); 109 110 this.setLayout(new BorderLayout()); 111 this.add(split, BorderLayout.CENTER); 112 } 113 114 /** 115 * Displays information about a given student 116 */ 117 private void displayStudent(String id) { 118 Student student = 119 this.book.getStudent(id).orElseThrow(() -> new IllegalStateException("No student with id " + id)); 120 this.studentPanel.displayStudent(student); 121 this.gradePanel.displayStudent(student); 122 } 123 124 /** 125 * Displays the names of the students in the class as either their 126 * full names or by the ids. 127 */ 128 private void displayStudents(int sortOrder) { 129 if (this.book == null) { 130 // Nothing to do 131 return; 132 } 133 134 this.model = new StudentsModel(this.book, sortOrder); 135 this.students.setModel(model); 136 } 137 138 /** 139 * Displays the contents of a given <code>GradeBook</code> 140 */ 141 void displayGradeBook(GradeBook book) { 142 this.book = book; 143 this.displayStudents(BY_ID); 144 this.classPanel.displayGradeBook(book); 145 this.gradePanel.displayAssignmentsFor(book); 146 this.studentPanel.clearContents(); 147 } 148 149 /** 150 * Test program 151 */ 152 public static void main(String[] args) { 153 final String fileName = args[0]; 154 155 GradeBook book = null; 156 try { 157 XmlGradeBookParser parser = new XmlGradeBookParser(fileName); 158 book = parser.parse(); 159 160 } catch (FileNotFoundException ex) { 161 System.err.println("** Could not find file: " + ex.getMessage()); 162 System.exit(1); 163 164 } catch (IOException ex) { 165 System.err.println("** IOException during parsing: " + ex.getMessage()); 166 System.exit(1); 167 168 } catch (ParserException ex) { 169 System.err.println("** Error during parsing: " + ex); 170 System.exit(1); 171 } 172 173 JFrame frame = new JFrame("GradeBookPanel test"); 174 GradeBookPanel bookPanel = new GradeBookPanel(frame); 175 bookPanel.displayGradeBook(book); 176 177 final GradeBook theBook = book; 178 frame.addWindowListener(new WindowAdapter() { 179 public void windowClosing(WindowEvent e) { 180 // Write changes to grade book back to file 181 try { 182 XmlDumper dumper = new XmlDumper(fileName); 183 dumper.dump(theBook); 184 185 } catch (IOException ex) { 186 System.err.println("** Error while writing XML file: " + ex); 187 } 188 189 System.exit(1); 190 } 191 }); 192 193 frame.getContentPane().add(bookPanel); 194 195 frame.pack(); 196 frame.setVisible(true); 197 } 198 199} 200 201/** 202 * Class used for displaying either student ids or their full names in 203 * a <code>JList</code> 204 */ 205@SuppressWarnings("serial") 206class StudentsModel extends AbstractListModel { 207 208 private ArrayList<String> ids; // ids in order 209 private ArrayList<String> data; // displayed names in order 210 211 public StudentsModel(final GradeBook book, final int sortedOrder) { 212 this.ids = new ArrayList<String>(); 213 this.data = new ArrayList<String>(); 214 215 SortedSet<Student> sortedStudents = new TreeSet<Student>(new Comparator<Student>() { 216 public int compare(Student s1, Student s2) { 217 if (sortedOrder == GradeBookPanel.BY_ID) { 218 // Sort by id 219 return s1.getId().compareTo(s2.getId()); 220 221 } else if (sortedOrder == GradeBookPanel.BY_NAME) { 222 // Sort by last name 223 String name1 = s1.getLastName(); 224 String name2 = s2.getLastName(); 225 226 if (name1 == null) { 227 name1 = s1.getId(); 228 } 229 230 if (name2 == null) { 231 name2 = s2.getId(); 232 } 233 234 if (name1.equalsIgnoreCase(name2)) { 235 name1 = s1.getFirstName(); 236 name2 = s2.getFirstName(); 237 238 if (name1 == null) { 239 name1 = s1.getId(); 240 } 241 242 if (name2 == null) { 243 name2 = s2.getId(); 244 } 245 246 } else { 247 return name1.compareTo(name2); 248 } 249 250 if (name1.equalsIgnoreCase(name2)) { 251 return s1.getId().compareTo(s2.getId()); 252 253 } else { 254 return name1.compareTo(name2); 255 } 256 257 } else { 258 throw new IllegalArgumentException("Unknown sort order: " 259 + sortedOrder); 260 } 261 } 262 263 public boolean equals(Object o) { 264 return o == this; 265 } 266 }); 267 268 book.forEachStudent(sortedStudents::add); 269 270 for (Student student : sortedStudents) { 271 this.ids.add(student.getId()); 272 273 if (sortedOrder == GradeBookPanel.BY_ID) { 274 // Display id 275 this.data.add(student.getId()); 276 277 } else if (sortedOrder == GradeBookPanel.BY_NAME) { 278 // Display full name 279 String name = student.getFullName(); 280 if (name == null || name.equals("")) { 281 name = student.getId(); 282 } 283 this.data.add(name); 284 285 } else { 286 throw new IllegalArgumentException("Unknown sort order: " 287 + sortedOrder); 288 } 289 } 290 291 } 292 293 /** 294 * Returns the id of the student at the given index 295 */ 296 public String getIdAt(int index) { 297 return (String) ids.get(index); 298 } 299 300 public Object getElementAt(int index) { 301 return data.get(index); 302 } 303 304 public int getSize() { 305 return this.data.size(); 306 } 307 308}