/**
*
*/
package eva.gui;
import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.imageio.ImageIO;
import javax.mail.MessagingException;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import org.joda.time.*;
import org.joda.time.format.PeriodFormat;
import org.joda.time.format.PeriodFormatter;
import eva.gui.mvc.*;
import eva.gui.mvc.ParentsEveningTreeModel.ModelMode;
import eva.schedule.*;
import eva.tools.StringEscapeUtils;
import eva.io.*;
import eva.algorithms.*;
/**
* @author SimonWagner
* @version 0.2
*
*/
public class MainFrame extends JFrame {
private EVAApplication application;
private JSplitPane splitPane;
private JTree parentsTree;
private JTree teachersTree;
private ParentsEveningTreeModel parentsTreeModell;
private ParentsEveningTreeModel teachersTreeModell;
private JTable appointmentsTable;
private AppointmentsTabelModel appointmentsTabelModel;
private JStatusBar statusBar = new JStatusBar();
private EveningSchedule eveningSchedule;
private Duration maxMaxWaitTime;
private Duration maxAvgWaitTime;
private URL exchangeWishesUrl;
private URL exchangeScheduleUrl;
private String password;
public MainFrame() {
application = EVAApplication.getApplication();
loadProperties();
initGUI();
}
private void loadProperties() {
Properties properties = application.getProperties();
maxMaxWaitTime = new Duration(Integer.parseInt(properties.getProperty("eva.optimization.maxmaxwaittime")));
maxAvgWaitTime = new Duration(Integer.parseInt(properties.getProperty("eva.optimization.maxavgwaittime")));
try {
exchangeWishesUrl = new URL(properties
.getProperty("eva.web.wishes-url"));
exchangeScheduleUrl = new URL(properties
.getProperty("eva.web.schedule-url"));
} catch (MalformedURLException e) {
showErrorMessageDialog("Fehlerhafte URLs in Einstellungen", "Fehler");
}
password = properties.getProperty("eva.web.password");
}
private void initGUI() {
initMenu();
initImages();
initTrees();
initTable();
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
getContentPane().add(splitPane);
JTabbedPane treeTabs = new JTabbedPane();
treeTabs.add("Eltern", new JScrollPane(parentsTree));
treeTabs.add("Lehrer", new JScrollPane(teachersTree));
splitPane.setLeftComponent(treeTabs);
splitPane.setRightComponent(new JScrollPane(appointmentsTable));
splitPane.setDividerLocation(150);
//status bar
add(statusBar, BorderLayout.SOUTH);
initListeners();
}
private void initTrees() {
//set up tree model
parentsTreeModell = new ParentsEveningTreeModel(ModelMode.PARENTS_TREE);
teachersTreeModell = new ParentsEveningTreeModel(ModelMode.TEACHERS_TREE);
//create checkers
ParentsEveningTreeModel.AppointableChecker conflictChecker =
new ParentsEveningTreeModel.AppointableChecker() {
@Override
public boolean check(Appointable appointable) {
return appointable.isConflicting() || !appointable.getSchedule().getVisitsChecklist().allVisitsDone();
}
};
ParentsEveningTreeModel.AppointableChecker warningsChecker =
new ParentsEveningTreeModel.AppointableChecker() {
/* note: this checker checks ONLY parents */
@Override
public boolean check(Appointable appointable) {
if(maxMaxWaitTime != null && maxAvgWaitTime != null && appointable instanceof Parent) {
Schedule schedule = appointable.getSchedule();
/*Parent parent = (Parent) appointable;
if(parent.getFirstName() == "Anke") {
System.out.println("STOP");
}*/
return schedule.getAvgWaitTime().isLongerThan(maxAvgWaitTime) ||
schedule.getMaxWaitTime().isLongerThan(maxMaxWaitTime);
}
else {
return false;
}
}
};
//set checkers
parentsTreeModell.setConflictChecker(conflictChecker);
parentsTreeModell.setWarningsChecker(warningsChecker);
teachersTreeModell.setConflictChecker(conflictChecker);
teachersTreeModell.setWarningsChecker(warningsChecker);
//create renderer
ParentsEveningTreeCellRenderer treeRenderer = new ParentsEveningTreeCellRenderer();
//set conflict and warning icons
treeRenderer.setConflictIcon(new ImageIcon(getClass().getResource("/eva/gui/images/conflict.png")));
treeRenderer.setWarningIcon(new ImageIcon(getClass().getResource("/eva/gui/images/warning.png")));
//set up trees
parentsTree = new JTree(parentsTreeModell);
parentsTree.setCellRenderer(treeRenderer);
parentsTree.setEditable(false);
teachersTree = new JTree(teachersTreeModell);
teachersTree.setCellRenderer(treeRenderer);
teachersTree.setEditable(false);
}
private void initTable() {
appointmentsTabelModel = new AppointmentsTabelModel();
appointmentsTable = new JTable(appointmentsTabelModel);
}
private void initListeners() {
//listener for changes of the selected tree node
TreeSelectionListener treeSelectionListener =
new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
Object source = e.getPath().getLastPathComponent();
if(source instanceof Appointable) {
Appointable appointable = (Appointable) source;
appointmentsTabelModel.setAppointable(appointable);
PeriodFormatter periodFormatter = PeriodFormat.getDefault();
String statusMessage =
"Wartezeit: " + periodFormatter.print(appointable.getSchedule().getWaitTime().toPeriod());
statusBar.setMessage(statusMessage);
}
}
};
parentsTree.addTreeSelectionListener(treeSelectionListener);
teachersTree.addTreeSelectionListener(treeSelectionListener);
}
@SuppressWarnings("serial")
private void initMenu() {
JMenuBar menuBar = new JMenuBar();
//file menu
JMenu fileMenu = new JMenu("Datei");
//file > open wishes
Action openWishes = new AbstractAction("Wünsche öffnen...") {
@Override
public void actionPerformed(ActionEvent e) {
openWishesFile();
}
};
//file > save schedule
Action saveSchedule = new AbstractAction("Zeitplan speichern...") {
@Override
public void actionPerformed(ActionEvent e) {
saveSchedule();
}
};
//file > exit
Action exitAction = new AbstractAction("Beenden") {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
};
fileMenu.add(openWishes);
fileMenu.add(saveSchedule);
fileMenu.addSeparator();
fileMenu.add(exitAction);
//schedule menu
JMenu scheduleMenu = new JMenu("Zeitplan");
//schedule > set optimization options
Action askOptimizationSettings = new AbstractAction("Optimierungs-Parameter festlegen...") {
@Override
public void actionPerformed(ActionEvent e) {
askOptimizationSettings();
}
};
//schedule > calculate schedule
Action calculateSchedule = new AbstractAction("Zeitplan berechnen...") {
@Override
public void actionPerformed(ActionEvent e) {
calculateSchedule();
}
};
scheduleMenu.add(askOptimizationSettings);
scheduleMenu.add(calculateSchedule);
//web interface menu
JMenu webinterfaceMenu = new JMenu("Webinterface");
//web interface > download wishes
Action downloadWishes = new AbstractAction("Wünsche herunterladen...") {
@Override
public void actionPerformed(ActionEvent e) {
downloadWishes();
}
};
//web interface > upload schedule
Action uploadSchedule = new AbstractAction("Zeitplan hochladen...") {
@Override
public void actionPerformed(ActionEvent e) {
uploadSchedule();
}
};
//web interface > send e-mails
Action sendEMails = new AbstractAction("E-Mails mit Benachrichtigung senden...") {
@Override
public void actionPerformed(ActionEvent e) {
sendEMails();
}
};
webinterfaceMenu.add(downloadWishes);
webinterfaceMenu.add(uploadSchedule);
webinterfaceMenu.add(sendEMails);
//options menu
JMenu optionsMenu = new JMenu("Optionen");
//options > settings
Action askSettings = new AbstractAction("Einstellungen...") {
@Override
public void actionPerformed(ActionEvent e) {
askSettings();
}
};
optionsMenu.add(askSettings);
menuBar.add(fileMenu);
menuBar.add(scheduleMenu);
menuBar.add(webinterfaceMenu);
menuBar.add(optionsMenu);
setJMenuBar(menuBar);
}
private void initImages() {
//load the application icon
try {
String iconsResourcePath = "/eva/gui/images/";
String iconNames[] = {
"eva-icon_16x16.png",
"eva-icon_32x32.png",
"eva-icon_64x64.png",
"eva-icon_128x128.png"
};
List<Image> icons = new ArrayList<Image>();
for(String iconName : iconNames) {
Image icon = ImageIO.read(getClass().getResource(iconsResourcePath + iconName));
icons.add(icon);
}
setIconImages(icons);
} catch (Exception e) {
System.err.println("could not read application icons: " + e.getMessage());
e.printStackTrace();
}
}
private void openWishesFile() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileFilter( new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() ||
file.getName().toLowerCase().endsWith( ".xml" );
}
@Override
public String getDescription() {
return "Wünsche-Datei (*.xml)";
}
});
int state = fileChooser.showOpenDialog(this);
if(state == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
try {
WishesReader wishesReader = new WishesReader(new FileInputStream(file));
wishesReader.read();
eveningSchedule = wishesReader.getEveningSchedule();
parentsTreeModell.setEveningSchedule(eveningSchedule);
teachersTreeModell.setEveningSchedule(eveningSchedule);
splitPane.resetToPreferredSizes();
} catch (FileNotFoundException e) {
showErrorMessageDialog("Datei konnte nicht gefunden werden!\n" + e.getLocalizedMessage(), "Fehler beim Lesen");
} catch (InvalidWishesFile e) {
showErrorMessageDialog("Ungültige Datei!\n" + e.getMessage(), "Fehler beim Lesen");
}
}
}
private void saveSchedule() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileFilter( new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() ||
file.getName().toLowerCase().endsWith( ".xml" );
}
@Override
public String getDescription() {
return "Zeitplan-Datei (*.xml)";
}
});
int state = fileChooser.showSaveDialog(this);
if(state == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
try {
EveningScheduleWriter writer = new EveningScheduleWriter(eveningSchedule, new FileOutputStream(file));
writer.write();
} catch (FileNotFoundException e) {
showErrorMessageDialog("Datei konnte nicht gefunden werden!\n" + e.getLocalizedMessage(), "Fehler beim Speichern");
} catch (IOException e) {
showErrorMessageDialog("Fehler beim schreiben!\n" + e.getMessage(), "Fehler beim Speichern");
}
}
}
private void calculateSchedule() {
if(!Evening.isEveningAvailable() || eveningSchedule == null) {
showErrorMessageDialog("Keine Wünsche vorhanden.", "Fehler - Zeitplan berechnen");
return;
}
eveningSchedule.clearSchedules();
GreedyScheduleGenerator generator = new GreedyScheduleGenerator(eveningSchedule);
try {
generator.run();
} catch (ImpossibleScheduleException e) {
showErrorMessageDialog("Zeitplan konnte nicht vollständig erstellt werden", "Unvollständiger Zeitplan");
return;
}
Collection <Parent> parents = eveningSchedule.getParents();
final int OPTIMIZER_RUNS = 10;
int optimizationDifferences = 0;
int oldWaitingTime;
for(int i = 1; i <= OPTIMIZER_RUNS; i++) {
optimizationDifferences = 0;
for(Parent parent : parents) {
oldWaitingTime = parent.getSchedule().getWaitingTimeInSlots();
LocalSearch optimizer = new LocalSearch(parent.getSchedule(), maxMaxWaitTime, maxAvgWaitTime);
try {
optimizer.run();
} catch (ImpossibleScheduleException e) {
showErrorMessageDialog("Zeitplan konnte nicht vollständig optimiert werden", "Unvollständiger Zeitplan");
break;
}
optimizationDifferences += oldWaitingTime - parent.getSchedule().getWaitingTimeInSlots();
}
System.out.println("completed optimizer run nr. " + i);
System.out.println("optimization difference: " + optimizationDifferences);
if(optimizationDifferences <= 0) {
break;
}
}
parentsTree.updateUI();
teachersTree.updateUI();
}
private void sendEMails() {
SendEMailsTask sendEMailsTask = new SendEMailsTask();
ProgressDialog dlg = new ProgressDialog(this, sendEMailsTask);
dlg.setTitle("E-Mails senden...");
dlg.setMessage("{1} von {2} Mails verschickt...");
sendEMailsTask.setProgressDialog(dlg);
dlg.showDialog();
}
private class SendEMailsTask implements Runnable {
ProgressDialog progressDialog;
@Override
public void run() {
sendEMailsThread();
}
public void setProgressDialog(ProgressDialog progressDialog) {
this.progressDialog = progressDialog;
}
private void sendEMailsThread() {
Properties properties = application.getProperties();
String address = properties.getProperty("eva.web.mail.address");
String host = properties.getProperty("eva.web.mail.host");
String user = properties.getProperty("eva.web.mail.user");
String password = properties.getProperty("eva.web.mail.password");
String parentsScheduleURL = properties.getProperty("eva.web.parent-schedule-url");
String parentsScheduleLink = StringEscapeUtils.escapeHtml(parentsScheduleURL);
if(eveningSchedule != null) {
Collection<Parent> parents = eveningSchedule.getParents();
ParentsNotificationMailer mailer = new ParentsNotificationMailer(host, user, password);
//set progress dialog
mailer.addProgressListener(progressDialog);
//set message
mailer.setMailInfo(address, properties.getProperty("eva.web.mail.msg.subject"));
mailer.setMessageTextPlain(properties.getProperty("eva.web.mail.msg.text"));
mailer.setMessageTextHtml(properties.getProperty("eva.web.mail.msg.html"));
//set magic words for the mailer
mailer.setMagicWord("PARENT_SCHEDULE_URL", parentsScheduleURL);
mailer.setMagicWord("PARENT_SCHEDULE_LINK", parentsScheduleLink);
//send mail
try {
mailer.connectMailServer();
} catch (MessagingException e) {
e.printStackTrace();
showErrorMessageDialog("Kann nicht zum Mail-Server verbinden\n" + e.getMessage(), "Mail senden fehlgeschlagen");
return;
}
int failCount = mailer.sendEMails(parents);
if(failCount > 0) {
showErrorMessageDialog(failCount + "Eltern konnte die Mail nicht zugestellt werden", "Mail senden fehlgeschlagen!");
}
}
}
}
private void uploadSchedule() {
File file = null;
//save the schedule in a temporary file
try {
file = File.createTempFile("schedule.xml", ".temp");
EveningScheduleWriter writer = new EveningScheduleWriter(eveningSchedule, new FileOutputStream(file));
writer.write();
} catch (IOException e) {
e.printStackTrace();
showErrorMessageDialog("Fehler beim Schreiben der tempor�ren Datei!\n" + e.getMessage(), "Fehler beim Upload");
return;
}
catch (Exception e) {
e.printStackTrace();
showErrorMessageDialog("Ein unerwarteter Fehler ist aufgetreten!\n" + e.getLocalizedMessage(), "Fehler - Download des Zeitplans");
return;
}
//upload the file
try {
HttpPostUpload uploader = new HttpPostUpload(exchangeScheduleUrl);
uploader.addUploadFile(new UploadFile("schedule", file, "text/xml"));
uploader.addFormField(new FormField("password", password));
uploader.upload();
} catch (UploadException e) {
e.printStackTrace();
showErrorMessageDialog("Fehler beim schreiben!\n" + e.getMessage(), "Fehler beim Upload");
return;
}
catch (Exception e) {
e.printStackTrace();
showErrorMessageDialog("Ein unerwarteter Fehler ist aufgetreten!\n" + e.getLocalizedMessage(), "Fehler - Download des Zeitplans");
return;
}
}
private void downloadWishes() {
try {
URLConnection connection = exchangeWishesUrl.openConnection();
connection.setDoInput(true);
InputStream stream = connection.getInputStream();
WishesReader wishesReader = new WishesReader(stream);
try {
wishesReader.read();
} catch (InvalidWishesFile e) {
e.printStackTrace();
showErrorMessageDialog("Ungültige Wünsche-Datei!", "Fehler - Download des Zeitplans");
return;
}
eveningSchedule = wishesReader.getEveningSchedule();
parentsTreeModell.setEveningSchedule(eveningSchedule);
teachersTreeModell.setEveningSchedule(eveningSchedule);
splitPane.resetToPreferredSizes();
}
catch (FileNotFoundException e) {
e.printStackTrace();
showErrorMessageDialog("Konnte angegebene Seite nicht finden!\n" + e.getLocalizedMessage(), "Fehler - Download des Zeitplans");
return;
}
catch (IOException e) {
e.printStackTrace();
showErrorMessageDialog("Konnte Zeitplan nicht herunterladen!\n" + e.getLocalizedMessage(), "Fehler - Download des Zeitplans");
return;
}
catch (Exception e) {
e.printStackTrace();
showErrorMessageDialog("Ein unerwarteter Fehler ist aufgetreten!\n" + e.getLocalizedMessage(), "Fehler - Download des Zeitplans");
return;
}
}
private void askOptimizationSettings() {
Properties properties = application.getProperties();
OptimizationSettingsDialog dlg = new OptimizationSettingsDialog(this);
dlg.setMaxMaxWaitTime(maxMaxWaitTime);
dlg.setMaxAvgWaitTime(maxAvgWaitTime);
if(dlg.showDialog()) {
maxMaxWaitTime = dlg.getMaxMaxWaitTime();
maxAvgWaitTime = dlg.getMaxAvgWaitTime();
properties.setProperty("eva.optimization.maxmaxwaittime", String.valueOf(maxMaxWaitTime.getMillis()));
properties.setProperty("eva.optimization.maxavgwaittime", String.valueOf(maxAvgWaitTime.getMillis()));
}
}
private void askSettings() {
SettingsDialog dlg = new SettingsDialog(this);
/*
* use a clone of the application settings, because we don't want to
* keep the old settings if Cancel is pressed
*/
Properties properties = (Properties) application.getProperties().clone();
dlg.setSettings(properties);
if(dlg.showDialog()) {
Properties newProperties = dlg.getSettings();
application.setProperties(newProperties);
loadProperties(); //update properties reference of this frame
}
}
private void showErrorMessageDialog(String msg, String title) {
JOptionPane.showMessageDialog(this, msg, title, JOptionPane.ERROR_MESSAGE);
}
}