001package edu.pdx.cs410J.grader.gradebook.ui;
002
003import edu.pdx.cs410J.ParserException;
004import edu.pdx.cs410J.grader.gradebook.Assignment;
005import edu.pdx.cs410J.grader.gradebook.GradeBook;
006import edu.pdx.cs410J.grader.gradebook.Notable;
007import edu.pdx.cs410J.grader.gradebook.XmlGradeBookParser;
008
009import javax.swing.*;
010import java.awt.*;
011import java.awt.event.WindowAdapter;
012import java.awt.event.WindowEvent;
013import java.io.FileNotFoundException;
014import java.io.IOException;
015import java.time.LocalDateTime;
016import java.time.format.DateTimeFormatter;
017import java.time.format.DateTimeParseException;
018import java.util.ArrayList;
019
020/**
021 * This panel is used to display and edit <code>Assignment</code>s.
022 */
023@SuppressWarnings("serial")
024public class AssignmentPanel extends JPanel {
025  private static final String QUIZ = "Quiz";
026  private static final String PROJECT = "Project";
027  private static final String OTHER = "Other";
028  private static final String OPTIONAL = "Optional";
029  private static final String POA = "POA";
030
031  static final String DATE_TIME_FORMAT_PATTERN = "M/d/yyyy h:mm a";
032  static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_PATTERN);
033
034  // GUI components we care about
035  private JTextField nameField;
036  private JComboBox<String> typeBox;
037  private JTextField pointsField;
038  private JTextField descriptionField;
039  private JTextField dueDateField;
040  private NotesPanel notes;
041
042  /**
043   * Creates and adds GUI components to a new
044   * <code>AssignmentPanel</code>.
045   */
046  public AssignmentPanel(boolean canCreate) {
047    this.setLayout(new BorderLayout());
048
049    // Panel containing information about an assignment
050    JPanel infoPanel = new JPanel();
051    infoPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
052    infoPanel.setLayout(new BorderLayout());
053
054    addAssignmentInformationWidgets(infoPanel);
055
056    this.add(infoPanel, BorderLayout.NORTH);
057
058    if (!canCreate) {
059      this.nameField.setEditable(false);
060    }
061
062    // Add a NotePanel
063    this.notes = new NotesPanel();
064    this.notes.setNotable(new Notable() {
065        private ArrayList<String> notes = new ArrayList<>();
066
067        @Override
068        public java.util.List<String> getNotes() {
069          return notes;
070        }
071
072        @Override
073        public void addNote(String note) {
074          notes.add(note);
075        }
076
077        @Override
078        public void removeNote(String note) {
079          notes.remove(note);
080        }
081      });
082    this.add(notes, BorderLayout.CENTER);
083  }
084
085  private void addAssignmentInformationWidgets(JPanel infoPanel) {
086    JPanel labels = new JPanel();
087    labels.setLayout(new GridLayout(0, 1));
088    labels.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
089    labels.add(new JLabel("Name:"));
090    labels.add(new JLabel("Max points:"));
091    labels.add(new JLabel("Type:"));
092    labels.add(new JLabel("Description:"));
093    labels.add(new JLabel("Due date:"));
094
095    JPanel fields = new JPanel();
096    fields.setLayout(new GridLayout(0, 1));
097    this.nameField = new JTextField(8);
098    fields.add(this.nameField);
099    this.pointsField = new JTextField(5);
100    fields.add(this.pointsField);
101
102    addTypeBox(fields);
103
104    this.descriptionField = new JTextField(20);
105    fields.add(this.descriptionField);
106    this.dueDateField = new JTextField(22);
107    fields.add(this.dueDateField);
108
109    infoPanel.add(labels, BorderLayout.WEST);
110    infoPanel.add(fields, BorderLayout.CENTER);
111  }
112
113  private void addTypeBox(JPanel fields) {
114    this.typeBox = new JComboBox<>();
115    this.typeBox.addItem(QUIZ);
116    this.typeBox.addItem(PROJECT);
117    this.typeBox.addItem(POA);
118    this.typeBox.addItem(OTHER);
119    this.typeBox.addItem(OPTIONAL);
120    fields.add(this.typeBox);
121  }
122
123  /**
124   * Creates a new <code>Assignment</code> based on the contents of
125   * this <code>AssignmentPanel</code>.
126   */
127  public Assignment createAssignment() {
128    // Get the name and max points of the assignment
129    String name = nameField.getText();
130    if (isNullOrEmpty(name)) {
131      return error("No assignment name specified");
132    }
133
134    String points = pointsField.getText();
135    if (isNullOrEmpty(points)) {
136      return error("No points value specified");
137    }
138
139    // Create a new Assignment object
140    try {
141      double d = Double.parseDouble(points);
142      Assignment newAssign = new Assignment(name, d);
143      this.notes.getNotable().getNotes().forEach(newAssign::addNote);
144
145      updateAssignment(newAssign);
146      this.notes.setNotable(newAssign);
147      return newAssign;
148      
149    } catch (NumberFormatException ex) {
150      return error(points + " is not a number");
151    }    
152  }
153
154  private boolean isNullOrEmpty(String name) {
155    return name == null || name.equals("");
156  }
157
158  private Assignment error(String message) {
159    JOptionPane.showMessageDialog(AssignmentPanel.this,
160      new String[]{message},
161      "Error",
162      JOptionPane.ERROR_MESSAGE);
163    return null;
164  }
165
166  /**
167   * Displays the contents of a given <code>Assignment</code> in this
168   * <code>AssignmentPanel</code>.
169   */
170  public void displayAssignment(Assignment assign) {
171    this.nameField.setText(assign.getName());
172    this.pointsField.setText(String.valueOf(assign.getPoints()));
173    Assignment.AssignmentType type = assign.getType();
174    if (type == Assignment.AssignmentType.QUIZ) {
175      this.typeBox.setSelectedItem(QUIZ);
176
177    } else if (type == Assignment.AssignmentType.PROJECT) {
178      this.typeBox.setSelectedItem(PROJECT);
179
180    } else if (type == Assignment.AssignmentType.OTHER) {
181      this.typeBox.setSelectedItem(OTHER);
182
183    } else if (type == Assignment.AssignmentType.OPTIONAL) {
184      this.typeBox.setSelectedItem(OPTIONAL);
185
186    } else if (type == Assignment.AssignmentType.POA) {
187      this.typeBox.setSelectedItem(POA);
188
189    } else {
190      String s = "Invalid assignment type: " + type;
191      throw new IllegalArgumentException(s);
192    }
193
194    String description = assign.getDescription();
195    if (isNotEmpty(description)) {
196      this.descriptionField.setText(description);
197
198    } else {
199      this.descriptionField.setText("");
200    }
201
202    LocalDateTime dueDate = assign.getDueDate();
203    if (dueDate != null) {
204      this.dueDateField.setText(DATE_TIME_FORMAT.format(dueDate));
205
206    } else {
207      this.dueDateField.setText("");
208    }
209
210    this.notes.setNotable(assign);
211  }
212
213  /**
214   * Fills in the contents of an <code>Assignment</code> based on the
215   * contents of this <code>AssignmentPanel</code>.
216   */
217  public void updateAssignment(Assignment assign) {
218    String points = pointsField.getText();
219    if (isNullOrEmpty(points)) {
220      error("No points value specified");
221      return;
222    }
223
224    try {
225      double d = Double.parseDouble(points);
226      assign.setPoints(d);
227      
228    } catch (NumberFormatException ex) {
229      error(points + " is not a number");
230      return;
231    }
232
233    setAssignmentType(assign);
234
235    String description = this.descriptionField.getText();
236
237    if (isNotEmpty(description)) {
238      assign.setDescription(description);
239    }
240
241    String dueDate = this.dueDateField.getText();
242    if (isNotEmpty(dueDate)) {
243      try {
244        assign.setDueDate(LocalDateTime.parse(dueDate.trim(), DATE_TIME_FORMAT));
245
246      } catch (DateTimeParseException ex) {
247        error(dueDate + " is not a validate date (" + DATE_TIME_FORMAT_PATTERN + ")");
248      }
249
250    } else {
251      assign.setDueDate(null);
252    }
253
254    // Adding notes is taken care of by the NotesPanel
255  }
256
257  private void setAssignmentType(Assignment assign) {
258    String type = (String) this.typeBox.getSelectedItem();
259
260    switch (type) {
261      case QUIZ:
262        assign.setType(Assignment.AssignmentType.QUIZ);
263        break;
264      case PROJECT:
265        assign.setType(Assignment.AssignmentType.PROJECT);
266        break;
267      case OTHER:
268        assign.setType(Assignment.AssignmentType.OTHER);
269        break;
270      case OPTIONAL:
271        assign.setType(Assignment.AssignmentType.OPTIONAL);
272        break;
273      case POA:
274        assign.setType(Assignment.AssignmentType.POA);
275        break;
276      default:
277        String s = "Unknown assignment type: " + type;
278        throw new IllegalArgumentException(s);
279    }
280  }
281
282  private boolean isNotEmpty(String description) {
283    return description != null && !description.equals("");
284  }
285
286  /**
287   * Test program
288   */
289  public static void main(String[] args) {
290    String fileName = args[0];
291    String assignName = args[1];
292
293    GradeBook book = null;
294    try {
295      XmlGradeBookParser parser = new XmlGradeBookParser(fileName);
296      book = parser.parse();
297
298    } catch (FileNotFoundException ex) {
299      System.err.println("** Could not find file: " + ex.getMessage());
300      System.exit(1);
301      
302    } catch (IOException ex) {
303      System.err.println("** IOException during parsing: " + ex.getMessage());
304      System.exit(1);
305
306    } catch (ParserException ex) {
307      System.err.println("** Error during parsing: " + ex);
308      System.exit(1);
309    }
310
311    Assignment assign = book.getAssignment(assignName);
312    if (assign == null) {
313      System.err.println("Not such assignment: " + assignName);
314      System.exit(1);
315    }
316
317    AssignmentPanel assignPanel = new AssignmentPanel(false);
318    assignPanel.displayAssignment(assign);
319
320    JFrame frame = new JFrame("AssignmentPanel test");
321    frame.addWindowListener(new WindowAdapter() {
322        @Override
323        public void windowClosing(WindowEvent e) {
324          System.exit(1);
325        }
326      });
327
328    frame.getContentPane().add(assignPanel);
329    
330    frame.pack();
331    frame.setVisible(true);
332  }
333}