View Javadoc

1   package at.ac.tuwien.ifs.bpse.basic.gui;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Color;
5   import java.awt.Dimension;
6   import java.awt.GridLayout;
7   import java.awt.Point;
8   import java.awt.Toolkit;
9   import java.awt.event.ActionEvent;
10  import java.awt.event.ActionListener;
11  import java.awt.event.ItemEvent;
12  import java.awt.event.ItemListener;
13  import java.awt.event.KeyAdapter;
14  import java.awt.event.KeyEvent;
15  import java.awt.event.MouseAdapter;
16  import java.awt.event.MouseEvent;
17  import java.awt.event.WindowAdapter;
18  import java.awt.event.WindowEvent;
19  import java.io.IOException;
20  import java.text.MessageFormat;
21  import java.util.List;
22  import java.util.ResourceBundle;
23  
24  import javax.swing.AbstractAction;
25  import javax.swing.BorderFactory;
26  import javax.swing.JButton;
27  import javax.swing.JComboBox;
28  import javax.swing.JFileChooser;
29  import javax.swing.JFrame;
30  import javax.swing.JLabel;
31  import javax.swing.JMenu;
32  import javax.swing.JMenuBar;
33  import javax.swing.JMenuItem;
34  import javax.swing.JOptionPane;
35  import javax.swing.JPanel;
36  import javax.swing.JScrollPane;
37  import javax.swing.JTable;
38  import javax.swing.ListSelectionModel;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.springframework.beans.factory.xml.XmlBeanFactory;
43  import org.springframework.core.io.ClassPathResource;
44  
45  import at.ac.tuwien.ifs.bpse.basic.dao.IStudentDAO;
46  import at.ac.tuwien.ifs.bpse.basic.dao.IStudentDAO.SortOrder;
47  import at.ac.tuwien.ifs.bpse.basic.domain.Student;
48  import at.ac.tuwien.ifs.bpse.basic.export_import.Export;
49  import at.ac.tuwien.ifs.bpse.basic.helper.Constants;
50  
51  /**
52   * The MainFrame is, as the name says, the main frame of this application. It
53   * utilizes a JPanel with a ContentPane and a BorderLayout.
54   * 
55   * @author The SE-Team
56   * @version 1.2
57   */
58  public class MainFrame extends JFrame implements ActionListener {
59  	/**
60  	 * serialVersionUID, generated by eclipse.
61  	 */
62  	private static final long serialVersionUID = -7167629968906183715L;
63  
64  	/**
65  	 * Retrieves the logger for this class.
66  	 */
67  	private static Log log = LogFactory.getLog(MainFrame.class);
68  	
69  	/**
70  	 * ResourceBundle to externalize the Strings used for the GUI components.
71  	 */
72  	private ResourceBundle messageBundle;
73  
74  	/**
75  	 * The Student Table Model, default order is Matrikel Number.
76  	 * 
77  	 * @see #initModels()
78  	 */
79  	private StudentenTableModel studentenTM;
80  
81  	/**
82  	 * The Table to display the Students from the Database.
83  	 */
84  	private JTable table;
85  
86  	/**
87  	 * Holds the menu entries for exporting data.
88  	 */
89  	private ExportMenuModel exportMenuModel;
90  
91  	private IStudentDAO studentDAO = null;
92  
93  	/**
94  	 * The XML Bean Factory from Spring.
95  	 * 
96  	 * @see #initDAO()
97  	 */
98  	private XmlBeanFactory xbf;
99  
100 	/**
101 	 * The Buttons for editing and deleting Students.
102 	 */
103 	private JButton editButton, deleteButton;
104 	
105 	/**
106 	 * The Labels for further information of Students.
107 	 */
108 	private JLabel id, name, surname, email, studentId;
109 
110 	/**
111 	 * The default Constructor for the MainFrame, initiating the DAO, the Models
112 	 * and the Components. Additionally it defines a ActionListener to clean up
113 	 * before exit.
114 	 * 
115 	 * @see #terminateApplication()
116 	 */
117 	public MainFrame() {
118 		super();
119 		initDAO();
120 		initMessageBundle();
121 		log.info("Initialising MainFrame");
122 		// Window Listener for Closing Frame
123 		addWindowListener(new WindowAdapter() {
124 			public void windowClosing(WindowEvent arg0) {
125 				super.windowClosing(arg0);
126 				terminateApplication();
127 			}
128 		});
129 		initModels();
130 		initComponents();
131 	}
132 
133 	/**
134 	 * Initializes the DAOs. Loads the XmlBean.
135 	 */
136 	private void initDAO() {
137 		ClassPathResource res = new ClassPathResource(Constants.SPRINGBEANS);
138 		xbf = new XmlBeanFactory(res);
139 		studentDAO = (IStudentDAO) xbf.getBean("StudentDAO");
140 	}
141 	
142 	/**
143 	 * Initializes the ResourceBundle to externalize the Strings for the
144 	 * components.
145 	 * 
146 	 * @see ResourceBundle
147 	 */
148 	private void initMessageBundle() {
149 		messageBundle = (ResourceBundle) xbf.getBean("resourceBundle");
150 	}
151 
152 	/**
153 	 * Initializes the Models. Creates the StudentenTableModel (for the Table)
154 	 * and loads the ExportMenuModel from the XmlBean.
155 	 * 
156 	 * @see StudentenTableModel
157 	 * @see ExportMenuModel
158 	 */
159 	private void initModels() {
160 		studentenTM = new StudentenTableModel(SortOrder.StudentId);
161 		exportMenuModel = (ExportMenuModel) xbf.getBean("ExportMenuModel");
162 	}
163 	
164 	/**
165 	 * Initializes all the components of the GUI.
166 	 */
167 	private void initComponents() {
168 		setTitle(messageBundle.getString("app.title"));
169 		// define menu bar and menus
170 		JMenuBar menuBar = new JMenuBar();
171 		JMenu fileMenu = new JMenu(messageBundle.getString("menu.lbl.file"));
172 		menuBar.add(fileMenu);
173 		JMenuItem exitMenuItem = new JMenuItem(messageBundle.getString("menu.lbl.exit"));
174 		exitMenuItem.setActionCommand(messageBundle.getString("menu.lbl.exit"));
175 		exitMenuItem.addActionListener(this);
176 		fileMenu.add(exitMenuItem);
177 		JMenu exportMenu = new JMenu(messageBundle.getString("menu.lbl.export"));
178 		menuBar.add(exportMenu);
179 		// retrieve list of export filters and define corresponding menu items and actions
180 		List<Export> exports = exportMenuModel.getExportFilter();
181 		for (Export export : exports) {
182 			exportMenu.add(new JMenuItem(new ExportAction(export.toString())));
183 		}
184 		setJMenuBar(menuBar);
185 		// define main panel
186 		JPanel mainPanel = new JPanel();
187 		getContentPane().add(mainPanel);
188 		// main panel is divided into two columns via GridLayout, 
189 		// the two columns themselves are divided into several areas via BorderLayout
190 		mainPanel.setLayout(new GridLayout(1, 2));
191 		// define left (sorting order box, information table, action buttons) and right (information box) panel
192 		JPanel leftColumnPanel = new JPanel();
193 		leftColumnPanel.setLayout(new BorderLayout());
194 		leftColumnPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
195 		JPanel leftColumnTopPanel = new JPanel();
196 		leftColumnTopPanel.setLayout(new GridLayout(0, 2, 5, 5));
197 		leftColumnTopPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
198 		leftColumnPanel.add(leftColumnTopPanel, BorderLayout.NORTH);
199 		JPanel leftColumnBottomPanel = new JPanel();
200 		leftColumnBottomPanel.setLayout(new GridLayout(0, 3, 5, 5));
201 		leftColumnBottomPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0));
202 		leftColumnPanel.add(leftColumnBottomPanel, BorderLayout.SOUTH);
203 		JPanel rightColumnPanel = new JPanel(new BorderLayout());
204 		rightColumnPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
205 		JPanel rightColumnTopPanel = new JPanel(new GridLayout(0, 1, 5, 5));
206 		rightColumnPanel.add(rightColumnTopPanel, BorderLayout.NORTH);
207 		mainPanel.add(leftColumnPanel);
208 		mainPanel.add(rightColumnPanel);
209 		// define Label for Drop Down Field in North Panel
210 		leftColumnTopPanel.add(new JLabel(messageBundle.getString("cb.lbl.sort")));
211 		// define Drop Down Field in North Panel
212 		JComboBox selectOrderCB = new JComboBox();
213 		selectOrderCB.setEditable(false);
214 		selectOrderCB.addItem(messageBundle.getString("cb.option.studentid"));
215 		selectOrderCB.addItem(messageBundle.getString("cb.option.lastname"));
216 		leftColumnTopPanel.add(selectOrderCB);
217 		selectOrderCB.addItemListener(new ItemListener() {
218 			public void itemStateChanged(ItemEvent ie) {
219 				if (ie.getStateChange() == ItemEvent.SELECTED) {
220 					log.debug("Order Combo State Changed: " + ie.getItem());
221 					sortOrderChanged(ie.getItem().toString());
222 				}
223 			}
224 		});
225 		// define table in center
226 		table = new JTable();
227 		table.setModel(studentenTM);
228 		table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
229 		table.setShowGrid(false);
230 		table.setGridColor(Color.LIGHT_GRAY);
231 		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
232 		table.addMouseListener(new MouseAdapter() {
233 			public void mouseClicked(MouseEvent e) {
234 				updateButtonStatus();
235 				updateInfoBoxPanel();
236 				if (e.getClickCount() >= 2) {
237 					int row = table.getSelectedRow();
238 					if (row > -1) {
239 						editStudent();
240 					}
241 				}
242 			}
243 		});
244 		table.addKeyListener(new KeyAdapter() {
245 			public void keyReleased(KeyEvent e) {
246 				updateButtonStatus();
247 				updateInfoBoxPanel();
248 			}
249 
250 			public void keyTyped(KeyEvent e) {
251 				if (e.getKeyChar() == KeyEvent.VK_DELETE) {
252 					deleteStudent();
253 				}
254 			}
255 		});
256 
257 		JScrollPane tableScrollPane = new JScrollPane(table);
258 		tableScrollPane.getViewport().setPreferredSize(table.getPreferredSize());
259 		tableScrollPane.setWheelScrollingEnabled(true);
260 		leftColumnPanel.add(tableScrollPane, BorderLayout.CENTER);
261 		// define buttons to manipulate students 
262 		editButton = new JButton(messageBundle.getString("btn.lbl.edit"));
263 		editButton.setEnabled(false);
264 		editButton.addActionListener(this);
265 		leftColumnBottomPanel.add(editButton);
266 		JButton createButton = new JButton(messageBundle.getString("btn.lbl.new"));
267 		createButton.addActionListener(this);
268 		leftColumnBottomPanel.add(createButton);
269 		deleteButton = new JButton(messageBundle.getString("btn.lbl.delete"));
270 		deleteButton.setEnabled(false);
271 		deleteButton.addActionListener(this);
272 		leftColumnBottomPanel.add(deleteButton);
273 		// define panel for info box
274 		JPanel infoBoxPanel = new JPanel();
275 		infoBoxPanel.setLayout(new GridLayout(0, 2, 5, 5));
276 		infoBoxPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
277 		JScrollPane infoBoxScrollPane = new JScrollPane(infoBoxPanel);
278 		rightColumnTopPanel.add(infoBoxScrollPane, BorderLayout.CENTER);
279 		// define labels for info box
280 		infoBoxPanel.add(new JLabel(messageBundle.getString("lbl.id")));
281 		id = new JLabel();
282 		infoBoxPanel.add(id);
283 		infoBoxPanel.add(new JLabel(messageBundle.getString("lbl.firstname")));
284 		name = new JLabel();
285 		infoBoxPanel.add(name);
286 		infoBoxPanel.add(new JLabel(messageBundle.getString("lbl.lastname")));
287 		surname = new JLabel();
288 		infoBoxPanel.add(surname);
289 		infoBoxPanel.add(new JLabel(messageBundle.getString("lbl.studentid")));
290 		studentId = new JLabel();
291 		infoBoxPanel.add(studentId);
292 		infoBoxPanel.add(new JLabel(messageBundle.getString("lbl.email")));
293 		email = new JLabel();
294 		infoBoxPanel.add(email);
295 	}
296 
297 	private void updateButtonStatus() {
298 		if (table.getSelectedRowCount() == 1) {
299 			editButton.setEnabled(true);
300 			deleteButton.setEnabled(true);
301 		} else {
302 			editButton.setEnabled(false);
303 			deleteButton.setEnabled(false);
304 		}
305 	}
306 	
307 	/**
308 	 * Handle changes of information in the InfoBoxPanel
309 	 */
310 	private void updateInfoBoxPanel() {
311 		int row = table.getSelectedRow();
312 		if (row > -1) {
313 			Student student = studentenTM.getStudentAt(row);
314 			id.setText( Integer.toString(student.getId()));
315 			name.setText(student.getFirstname());
316 			surname.setText(student.getLastname());
317 			studentId.setText(student.getMatnr());
318 			email.setText(student.getEmail());
319 		}
320 	}
321 
322 	private void placeNewFrame(JFrame frame) {
323 		frame.pack();
324 
325 		Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
326 		Point p = this.getLocation();
327 		// First try:
328 		// new Window right next to this one:
329 		if (screen.width > (this.getLocation().x + this.getWidth() + frame
330 				.getWidth())) {
331 			p.x += this.getWidth();
332 		}
333 		// Second try:
334 		// new Window left of this one:
335 		else if (this.getLocation().x - frame.getWidth() >= 0) {
336 			p.x -= frame.getWidth();
337 		}
338 		// Fallthrough:
339 		// Place centered over this frame
340 		else {
341 			p.x += (this.getWidth() - frame.getWidth()) / 2;
342 			p.y += (this.getHeight() - frame.getHeight()) / 2;
343 		}
344 		frame.setLocation(p);
345 	}
346 
347 	public void actionPerformed(ActionEvent ae) {
348 		String cmd = ae.getActionCommand();
349 		log.debug("Action Performed \"" + cmd + "\"");
350 		if (cmd.equals(messageBundle.getString("menu.lbl.exit"))) {
351 			terminateApplication();
352 		} else if (cmd.equals(messageBundle.getString("btn.lbl.new"))) {
353 			createStudent();
354 		} else if (cmd.equals(messageBundle.getString("btn.lbl.edit"))) {
355 			editStudent();
356 		} else if (cmd.equals(messageBundle.getString("btn.lbl.delete"))) {
357 			deleteStudent();
358 		}
359 	}
360 
361 	// Business Methods
362 
363 	/**
364 	 * Handle a create-request from the GUI
365 	 */
366 	private void createStudent() {
367 		EditStudentFrame esf = new EditStudentFrame();
368 		placeNewFrame(esf);
369 		esf.pack();
370 		esf.addUpdateListener(new UpdateListener());
371 		esf.setVisible(true);
372 	}
373 
374 	/**
375 	 * Handle a edit-request from the GUI
376 	 *
377 	 */
378 	private void editStudent() {
379 		int row = table.getSelectedRow();
380 		if (row > -1) {
381 			EditStudentFrame esf = new EditStudentFrame(studentenTM
382 					.getStudentAt(row), EditStudentFrame.Mode.Update);
383 			placeNewFrame(esf);
384 			esf.pack();
385 			esf.addUpdateListener(new UpdateListener());
386 			esf.setVisible(true);
387 		}
388 	}
389 
390 	/**
391 	 * Handle a delete-request from the GUI
392 	 *
393 	 */
394 	private void deleteStudent() {
395 		int row = table.getSelectedRow();
396 		if (row > -1) {
397 			Student victim = studentenTM.getStudentAt(row);
398 			// support full I18n with patterns 
399 			Object [] messageArgs = {victim.getFullname(), victim.getMatnr()};
400 			MessageFormat formatter = new MessageFormat("");
401 			formatter.applyPattern(messageBundle.getString("pattern"));
402 			int yesno = JOptionPane.showConfirmDialog(this, formatter.format(messageArgs), messageBundle.getString("delete.lbl.title"),
403 					JOptionPane.YES_NO_OPTION);
404 			if (yesno == 0) {
405 				if (studentDAO.deleteStudent(victim.getId())) {
406 					studentenTM.reload();
407 					JOptionPane.showMessageDialog(this, messageBundle.getString("delete.lbl.success"));
408 				} else {
409 					JOptionPane.showMessageDialog(this, messageBundle.getString("delete.lbl.error"));
410 				}
411 			}
412 		}
413 	}
414 
415 	/**
416 	 * Clean up and exit
417 	 */
418 	private void terminateApplication() {
419 		log.info("Closing MainFrame and Exit");
420 		System.exit(0);
421 	}
422 
423 	/**
424 	 * Change order and reload
425 	 * 
426 	 * @param order
427 	 *            the new order
428 	 * @see StudentenTableModel#setSortOrder(String)
429 	 */
430 	private void sortOrderChanged(String order) {
431 		log.info("Sort Order Changed to \"" + order + "\"");
432 		if(order.equals(messageBundle.getString("cb.option.studentid"))) {
433 			studentenTM.setSortOrder(SortOrder.StudentId);
434 		} else if (order.equals(messageBundle.getString("cb.option.lastname"))) {
435 			studentenTM.setSortOrder(SortOrder.LastName);
436 		}
437 	}
438 
439 	/**
440 	 * Export current view to XML or HTML file. Before saving a
441 	 * FileSelect-Dialog is shown to choose a filename and a location to save.
442 	 */
443 	private void export() {
444 		Export export = (Export) exportMenuModel.getSelectedItem();
445 		// Create a file dialog to choose filename for export
446 		JFileChooser jfc = new JFileChooser();
447 		jfc.setDialogTitle(messageBundle.getString("export.lbl.filename"));
448 		jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
449 		int returnVal = jfc.showSaveDialog(this);
450 		// check if Export was confirmed or canceled by user
451 		if (returnVal == JFileChooser.APPROVE_OPTION) {
452 			String filename = jfc.getSelectedFile().getPath();
453 			String extension = export.getExtension();
454 			if (!extension.startsWith(".")) {
455 				extension = "." + extension;
456 			}
457 			if (!filename.endsWith(extension)) {
458 				filename += extension;
459 			}
460 			log.info("Filename for export = \"" + filename + "\"");
461 			try {
462 				export.write(studentenTM.getStudenten(), filename);
463 				JOptionPane.showMessageDialog(this,
464 						messageBundle.getString("export.lbl.success"), messageBundle.getString("export.lbl.title"),
465 						JOptionPane.INFORMATION_MESSAGE);
466 			} catch (IOException e) {
467 				JOptionPane.showMessageDialog(this,
468 						messageBundle.getString("export.lbl.error"),
469 						messageBundle.getString("export.lbl.title"), JOptionPane.WARNING_MESSAGE);
470 				log.error("File Writing Error: " + e);
471 			}
472 		}
473 	}
474 
475 	/**
476 	 * UpdateListener waits for database-changes and udates the table.
477 	 * 
478 	 * @author The SE-Team
479 	 * @see EditStudentFrame#addUpdateListener(ActionListener)
480 	 * @since 1.1
481 	 */
482 	class UpdateListener implements ActionListener {
483 		public void actionPerformed(ActionEvent e) {
484 			log.info("Update studentTM");
485 			studentenTM.reload();
486 		}
487 	}
488 	
489 	/**
490 	 * ExportAction provides the dynamic actions for the export menu.
491 	 * 
492 	 * @author The SE-Team
493 	 * @since 1.2
494 	 *
495 	 */
496 	@SuppressWarnings("serial")
497 	class ExportAction extends AbstractAction {
498 		public ExportAction(String name) {
499 			super(name);
500 		}
501 
502 		public void actionPerformed(ActionEvent e) {
503 			log.info("ExportAction for menu item " + e.getActionCommand() + " called.");
504 			exportMenuModel.setSelectedItem(e.getActionCommand());
505 			export();
506 		}
507 	}
508 }