/* * This file is part of ADDIS (Aggregate Data Drug Information System). * ADDIS is distributed from http://drugis.org/. * Copyright © 2009 Gert van Valkenhoef, Tommi Tervonen. * Copyright © 2010 Gert van Valkenhoef, Tommi Tervonen, Tijs Zwinkels, * Maarten Jacobs, Hanno Koeslag, Florin Schimbinschi, Ahmad Kamal, Daniel * Reid. * Copyright © 2011 Gert van Valkenhoef, Ahmad Kamal, Daniel Reid, Florin * Schimbinschi. * Copyright © 2012 Gert van Valkenhoef, Daniel Reid, Joël Kuiper, Wouter * Reckman. * Copyright © 2013 Gert van Valkenhoef, Joël Kuiper. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.drugis.addis.gui; import java.awt.Container; import java.awt.event.ActionEvent; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.KeyStroke; import javax.swing.ToolTipManager; import javax.swing.UIManager; import org.drugis.addis.entities.Domain; import org.drugis.addis.entities.DomainManager; import org.drugis.addis.presentation.DomainChangedModel; import org.drugis.addis.util.jaxb.JAXBHandler; import org.drugis.addis.util.jaxb.JAXBHandler.XmlFormatType; import org.drugis.common.ImageLoader; import org.drugis.common.beans.AbstractObservable; import org.drugis.common.gui.ErrorDialog; import org.drugis.common.gui.FileLoadDialog; import org.drugis.common.gui.GUIHelper; import org.drugis.common.gui.ImageExporter; import org.drugis.common.threading.ThreadHandler; import com.sun.org.apache.xerces.internal.util.XMLChar; @SuppressWarnings("serial") public class Main extends AbstractObservable { public static final ImageLoader IMAGELOADER = new ImageLoader("/org/drugis/addis/gfx/"); public enum Examples { DEPRESSION("Severe depression", "depressionExample.addis"), HYPERTENSION("Hypertension", "hypertensionExample.addis"); final public String file; final public String name; Examples(String name, String file) { this.name = name; this.file = file; } public static Examples findByName(String name) { for(Examples example : values()) { if (example.name.equals(name)) return example; } throw new IllegalArgumentException("Could not find example for " + name); } public static String findFileName(String name) { for(Examples example : values()) { if (example.name.equals(name)) return example.file; } throw new IllegalArgumentException("Could not find example data file for " + name); } } private static final String PRINT_SCREEN = "F12"; // control p ... alt x ... etc static final String DISPLAY_EXAMPLE = "Example Data"; static final String DISPLAY_NEW = "New File"; public static final String PROPERTY_DISPLAY_NAME = "displayName"; private static final String BUG_REPORTING_TEXT = "<html>This is probably a bug. " + "Help us improve ADDIS by reporting this problem to us.<br/>" + "Attaching the stack trace and the .addis data file would be very helpful.<br/>" + "See <a href=\"http://drugis.org/addis-bug\">http://drugis.org/addis-bug</a> for instructions.<br/><br/>" + "Consider restarting ADDIS.</html>"; private static AddisWindow s_window; private DomainManager d_domainMgr; private String d_curFilename = null; private String d_displayName = null; private DomainChangedModel d_domainChanged; private final boolean d_headless; private XmlFormatType d_xmlType = null; public Main(String[] args, boolean headless) { d_headless = headless; if (!d_headless) { GUIHelper.initializeLookAndFeel(); UIManager.put("Button.defaultButtonFollowsFocus", Boolean.TRUE); ToolTipManager.sharedInstance().setInitialDelay(0); GUIFactory.configureJFreeChartLookAndFeel(); } initializeDomain(); if (args.length > 0) { d_curFilename = args[0]; } } public static void bindPrintScreen(Container container) { final JComponent content = (JComponent) container; content.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(PRINT_SCREEN), "printWindow"); content.getActionMap().put("printWindow", new AbstractAction("printWindow") { public void actionPerformed(ActionEvent evt) { try { ImageExporter.writeImage(content, content, content.getWidth(), content.getHeight()); } catch (Exception e) { throw new RuntimeException("Error writing image: " + e.getMessage(), e); } } }); } protected void showWelcome() { final WelcomeDialog welcome = new WelcomeDialog(this); welcome.setLocationByPlatform(true); welcome.setVisible(true); } protected void quitApplication() { if(isDataChanged()) { if(s_window == null || s_window.saveChangesDialog()) { quit(); } } else { quit(); } } private void quit() { if (s_window != null) { s_window.dispose(); } System.exit(0); } public static class XMLStreamFilter extends FilterOutputStream { public XMLStreamFilter(OutputStream out) { super(out); } @Override public void write(int b) throws IOException { if (XMLChar.isValid((char)b)) { super.write(b); } else { System.err.println("Removing invalid character while marshalling XML: " + (char)b); } } public static List<Character> getCharacters() { List<Character> invalids = new ArrayList<Character>(); for (int i = 0x0; i <= 0xFFFD; i++) { if (!((i == 0x9) || (i == 0xA) || (i == 0xD) || ((i >= 0x20) && (i <= 0xD7FF)))) { invalids.add((char) i); } } return invalids; } } private void saveDomainToXMLFile(String fileName) throws IOException { d_domainMgr.saveXMLDomain(new File(fileName)); } private void initializeDomain() { d_domainMgr = new DomainManager(); attachDomainChangedModel(); } public void loadExampleDomain(String exampleFile) { try { loadDomainFromXMLResource(exampleFile); } catch (Exception e) { throw new RuntimeException("Error loading default data: " + e.getMessage(), e); } setDisplayName(DISPLAY_EXAMPLE); setDataChanged(false); showMainWindow(); } public Domain getDomain() { return d_domainMgr.getDomain(); } private boolean loadDomainFromXMLFile(String fileName) { File f = new File(fileName); if (f.exists() && f.isFile()) { try { FileInputStream in = new FileInputStream(f); d_xmlType = loadDomainFromInputStream(in); } catch (Exception e) { ErrorDialog.showDialog(e, "Error loading file", "Error loading data from \"" + fileName + "\"", false); return false; } if (!d_xmlType.isValid()) { JOptionPane.showMessageDialog(s_window, "The file you are attempting to load is not formatted as a valid ADDIS XML file.", "Error loading file", JOptionPane.ERROR_MESSAGE); return false; } else if (d_xmlType.isFuture()) { JOptionPane.showMessageDialog(s_window, "The XML file was created with a newer version of ADDIS than you are using. Please download the new version to read it.", "Error loading file", JOptionPane.ERROR_MESSAGE); return false; } else if (d_xmlType.isLegacy()) { askToConvertToNew(fileName); return true; } else { setFileNameAndReset(fileName); return true; } } else { JOptionPane.showMessageDialog(s_window, "File \"" + fileName + "\" not found.", "Error loading file", JOptionPane.ERROR_MESSAGE); return false; } } private XmlFormatType loadDomainFromInputStream(InputStream in) throws IOException { BufferedInputStream fis = new BufferedInputStream(in); XmlFormatType xmlType = JAXBHandler.determineXmlType(fis); if (!xmlType.isValid() || xmlType.isFuture()) { return xmlType; } else if (xmlType.isLegacy()) { d_domainMgr.loadLegacyXMLDomain(fis); } else { d_domainMgr.loadXMLDomain(fis, xmlType.getVersion()); } attachDomainChangedModel(); return xmlType; } private void attachDomainChangedModel() { d_domainChanged = new DomainChangedModel(getDomain(), d_xmlType != null ? d_xmlType.isPast() : false); } private void askToConvertToNew(String fileName) { int response = JOptionPane.showConfirmDialog(s_window, "The data format for ADDIS has changed.\n\nWould you like to save this file in the new file format (.addis)?", "Save in new format?", JOptionPane.YES_NO_OPTION); if (response == JOptionPane.OK_OPTION) { if(fileName.endsWith(".xml")) { fileName = fileName.replace(".xml", ".addis"); } else { fileName += ".addis"; } saveDomainToFile(fileName); setFileNameAndReset(fileName); } else { setCurFilename(null); setDisplayName(DISPLAY_NEW); setDataChanged(true); } } private void loadDomainFromXMLResource(String fileName) throws IOException, ClassNotFoundException { InputStream fis = Main.class.getResourceAsStream("/org/drugis/addis/" + fileName); d_xmlType = loadDomainFromInputStream(fis); } void newFileActions() { newDomain(); resetDomain(); showMainWindow(); } public void newDomain() { d_domainMgr.resetDomain(); // Create an empty domain. setCurFilename(null); setDisplayName(DISPLAY_NEW); setDataChanged(false); } private void resetDomain() { ThreadHandler.getInstance().clear(); // Terminate all running threads. attachDomainChangedModel(); disposeMainWindow(); } private void disposeMainWindow() { if (s_window != null) { s_window.dispose(); s_window = null; } } public int fileLoadActions() { final boolean[] loaded = { false }; FileLoadDialog d = new FileLoadDialog(s_window, new String[][] {{"addis", "xml"}, {"addis"}, {"xml"}}, new String[] {"ADDIS or legacy XML files", "ADDIS data files", "ADDIS legacy XML files"}) { @Override public void doAction(String path, String extension) { loaded[0] = loadDomainFromFile(path); } }; d.loadActions(); return loaded[0] ? d.getReturnValue() : JFileChooser.ERROR_OPTION; } public boolean loadDomainFromFile(String path) { if (loadDomainFromXMLFile(path)) { resetDomain(); showMainWindow(); return true; } return false; } public void saveDomainToFile(String path) { try { saveDomainToXMLFile(path); setFileNameAndReset(path); } catch (Exception e) { throw new RuntimeException("Error saving data to " + path + ": " + e.getMessage(), e); } } private void setFileNameAndReset(String path) { setCurFilename(path); int x = path.lastIndexOf("."); int y = path.lastIndexOf("/"); setDisplayName(path.substring(y+1, x)); setDataChanged(false); } public static void main(final String[] args) { new Thread(new Runnable() { public void run() { JAXBHandler.JAXB.getInstance(); // Initializes JAXBContext } }).start(); GUIHelper.startApplicationWithErrorHandler(new Runnable() { public void run() { Main main = new Main(args, false); main.startGUI(); } }, BUG_REPORTING_TEXT); } private void startGUI() { if (d_curFilename == null) { showWelcome(); } else { try { loadDomainFromXMLFile(d_curFilename); } catch (Exception e) { ErrorDialog.showDialog(e, "Could not load file.", e.getMessage(), false); } finally { showMainWindow(); } } } public void leftTreeFocus(Object node) { s_window.leftTreeFocus(node); } public void setCurFilename(String curFilename) { d_curFilename = curFilename; } public String getCurFilename() { return d_curFilename; } public void setDataChanged(boolean dataChanged) { d_domainChanged.setValue(dataChanged); } public boolean isDataChanged() { return d_domainChanged.getValue(); } public void showMainWindow() { if (!d_headless) { s_window = new AddisWindow(this, getDomain()); s_window.pack(); s_window.setLocationByPlatform(true); s_window.setVisible(true); } } /** * @return returns the only AddisWindow currently in existence for this process */ public static AddisWindow getMainWindow() { return s_window; } public DomainChangedModel getDomainChangedModel() { return d_domainChanged; } private void setDisplayName(String displayName) { String oldValue = d_displayName; d_displayName = displayName; firePropertyChange(PROPERTY_DISPLAY_NAME, oldValue, displayName); } public String getDisplayName() { return d_displayName; } public DomainManager getDomainManager() { return d_domainMgr; } }