/**
* Copyright (C) 2006, Laboratorio di Valutazione delle Prestazioni - Politecnico di Milano
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package jmt.gui.common.xml;
import java.awt.Component;
import java.awt.HeadlessException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import jmt.framework.xml.XMLUtils;
import jmt.gui.common.CommonConstants;
import jmt.gui.common.Defaults;
import jmt.gui.common.definitions.CommonModel;
import jmt.gui.common.definitions.ModelConverter;
import jmt.gui.common.definitions.StoredResultsModel;
import jmt.gui.exact.ExactModel;
import jmt.gui.jaba.JabaModel;
import org.w3c.dom.Document;
/**
* <p>Title: Model Loader</p>
* <p>Description: This class provides unified load/save functionality
* for every JMT application</p>
*
* @author Bertoli Marco
* Date: 15-feb-2006
* Time: 15.36.51
*/
public class ModelLoader {
/**
* Filters for input files
*/
public static final JmtFileFilter JMODEL = new JmtFileFilter(".jsimg;.jmodel", "JSIMgraph data file");
public static final JmtFileFilter JSIM = new JmtFileFilter(".jsimw;.jsim", "JSIMwiz data file");
public static final JmtFileFilter JMVA = new JmtFileFilter(".jmva", "JMVA data file");
public static final JmtFileFilter JABA = new JmtFileFilter(".jaba", "JABA data file");
public static final JmtFileFilter XML = new JmtFileFilter(".xml", "Engine XML i/o file");
public static final JmtFileFilter ALL = new JmtFileFilter(".jsimg;.jsimw;.jmva;.jaba;.xml;.jsim;.jmodel", "All JMT data files");
public static final JmtFileFilter ALL_NOTJABA = new JmtFileFilter(".jsimg;.jsimw;.jmva;.xml;.jsim;.jmodel", "All compatible JMT data files");
/**
* Constants used for output
*/
public static final int SUCCESS = 0;
public static final int CANCELLED = 1;
public static final int FAILURE = 2;
public static final int WARNING = 3;
/**
* Constants to define xml types
*/
protected static final int XML_SIM = 0;
protected static final int XML_ARCHIVE = 1;
protected static final int XML_MVA = 2;
protected static final int XML_JABA = 5;
protected static final int XML_RES_SIM = 3;
protected static final int XML_RES_GUI = 4;
protected static final int FILE_UNKNOWN = 255;
/**
* Failure motivations
*/
protected static final String FAIL_UNKNOWN = "Unknown input file format";
protected static final String FAIL_CONVERSION = "Input file is recognized but cannot be converted "
+ "to work within this application. Please open it with ";
// Better to move this element elsewere...
protected static final String XML_MVA_ROOT = "model";
protected JmtFileChooser dialog;
protected JmtFileFilter defaultFilter;
// Motivation of last failure
protected String failureMotivation;
// Warnings found during last conversion
protected List<String> warnings = new ArrayList<String>();
protected String fileFormat;
protected XMLUtils xmlutils = new XMLUtils();
/**
* Initialize a new ModelLoader with specified default file filter (chosen between
* constrants in this class)
* @param defaultFilter default file filter for current application (used to detect application type)
*/
public ModelLoader(JmtFileFilter defaultFilter) {
this.defaultFilter = defaultFilter;
// Initialize filechooser dialog
dialog = new JmtFileChooser(defaultFilter);
}
/**
* Gets the motivation of last failure
* @return the motivation of last failure
*/
public String getFailureMotivation() {
return failureMotivation;
}
/**
* Gets a vector containing warnings of last performed operation
* @return a Vector of String with every found warning
*/
public List<String> getLastWarnings() {
return warnings;
}
/**
* @return the format of opened input file
*/
public String getInputFileFormat() {
return fileFormat;
}
// --- Methods used to load models ------------------------------------------------------------
/**
* Shows a Load window and loads chosen model inside specified model data file
* @param modelData data file where information should be stored. Note that <b>its type
* must be compatible with defaultFilter chosen in the constructor</b>, otherwise a
* ClassCastException will be thrown
* @param parent parent component of loading window
* @return SUCCESS on success, CANCELLED if loading is cancelled,
* FAILURE if an error occurs and WARNING is one or more warnings are generated due to
* format conversion
* @throws ClassCastException if modelData is not of instance of the correct class
* @see #getFailureMotivation getFailureMotivation()
*/
public int loadModel(Object modelData, Component parent) {
warnings.clear();
int status;
status = this.showOpenDialog(parent);
if (status == JFileChooser.CANCEL_OPTION) {
return CANCELLED;
} else if (status == JFileChooser.ERROR_OPTION) {
failureMotivation = "Error selecting input file";
return FAILURE;
}
File file = dialog.getSelectedFile();
try {
if (defaultFilter == JMODEL || defaultFilter == JSIM) {
StoredResultsModel srm;
// Handles loading of JSIM/JMODEL models
switch (getXmlFileType(file.getAbsolutePath())) {
case XML_SIM:
XMLReader.loadModel(file, (CommonModel) modelData);
fileFormat = CommonConstants.SIMENGINE;
break;
case XML_ARCHIVE:
XMLArchiver.loadModel((CommonModel) modelData, file);
fileFormat = CommonConstants.JSIM;
break;
case XML_MVA:
ExactModel tmp = new ExactModel();
tmp.loadDocument(xmlutils.loadXML(file));
warnings.addAll(ModelConverter.convertJMVAtoJSIM(tmp, (CommonModel) modelData));
fileFormat = CommonConstants.JMVA;
break;
case XML_JABA:
//TODO implement bridge JABA --> JSIM
failureMotivation = FAIL_CONVERSION + "JABA.";
fileFormat = CommonConstants.JABA;
return FAILURE;
case XML_RES_SIM:
srm = new StoredResultsModel();
XMLResultsReader.loadModel(srm, file);
((CommonModel) modelData).setSimulationResults(srm);
warnings.add("Loaded file contained simulation results only. Associated queuing network model is not available. "
+ "Results can be shown by selecting \"Show Results\" icon.");
fileFormat = CommonConstants.SIMENGINE;
break;
case XML_RES_GUI:
srm = new StoredResultsModel();
XMLResultsReader.loadGuiModel(srm, file);
((CommonModel) modelData).setSimulationResults(srm);
warnings.add("Loaded file contained simulation results only. Associated queuing network model is not available. "
+ "Results can be shown by selecting \"Show Results\" icon.");
fileFormat = CommonConstants.SIMENGINE;
break;
default:
failureMotivation = FAIL_UNKNOWN;
return FAILURE;
}
} else if (defaultFilter == JMVA) {
// Handles loading of JMVA models
CommonModel tmp = new CommonModel();
switch (getXmlFileType(file.getAbsolutePath())) {
case XML_SIM:
XMLReader.loadModel(file, tmp);
warnings.addAll(ModelConverter.convertJSIMtoJMVA(tmp, (ExactModel) modelData));
fileFormat = CommonConstants.SIMENGINE;
break;
case XML_ARCHIVE:
XMLArchiver.loadModel(tmp, file);
warnings.addAll(ModelConverter.convertJSIMtoJMVA(tmp, (ExactModel) modelData));
fileFormat = CommonConstants.JSIM;
break;
case XML_JABA:
//TODO implement bridge JABA --> JMVA
failureMotivation = FAIL_CONVERSION + "JABA.";
fileFormat = CommonConstants.JABA;
return FAILURE;
case XML_MVA:
((ExactModel) modelData).loadDocument(xmlutils.loadXML(file));
fileFormat = CommonConstants.JMVA;
break;
case XML_RES_SIM:
case XML_RES_GUI:
// This is silly to be opened in JMVA...
failureMotivation = FAIL_CONVERSION + "JSIM or JMODEL.";
fileFormat = CommonConstants.SIMENGINE;
return FAILURE;
default:
failureMotivation = FAIL_UNKNOWN;
return FAILURE;
}
} else if (defaultFilter == JABA) {
// Handles loading of JABA models
switch (getXmlFileType(file.getAbsolutePath())) {
case XML_SIM:
//TODO implement bridge JSIM --> JABA
case XML_ARCHIVE:
//TODO implement bridge JSIM --> JABA
failureMotivation = FAIL_CONVERSION + "JSIM or JMODEL.";
return FAILURE;
case XML_MVA:
//TODO implement bridge JMVA --> JABA
failureMotivation = FAIL_CONVERSION + "JMVA.";
return FAILURE;
case XML_JABA:
((JabaModel) modelData).loadDocument(xmlutils.loadXML(file));
break;
case XML_RES_SIM:
case XML_RES_GUI:
// This is silly to be opened in JABA...
failureMotivation = FAIL_CONVERSION + "JSIM or JMODEL.";
return FAILURE;
default:
failureMotivation = FAIL_UNKNOWN;
return FAILURE;
}
}
} catch (Exception e) {
e.printStackTrace();
failureMotivation = e.getClass().getName() + ": " + e.getMessage();
return FAILURE;
}
// If no warnings were found, report success
if (warnings.size() > 0) {
return WARNING;
} else {
return SUCCESS;
}
}
/**
* Returns name of selected file for i/o operations
* @return name of selected file for open or save
*/
public File getSelectedFile() {
return dialog.getSelectedFile();
}
// --------------------------------------------------------------------------------------------
// --- Methods used to save models ------------------------------------------------------------
/**
* Saves specified model into specified file or shows save as window if file is null
* @param modelData data file where information should be stored. Note that <b>its type
* must be compatible with defaultFilter chosen in the constructor</b>, otherwise a
* ClassCastException will be thrown
* @param parent parent window that will own the save as dialog
* @param file location where pecified model must be saved or null if save as must be shown
* @return SUCCESS on success, CANCELLED if loading is cancelled,
* FAILURE if an error occurs
* @throws ClassCastException if modelData is not of instance of the correct class
* @see #getFailureMotivation getFailureMotivation()
*/
public int saveModel(Object modelData, Component parent, File file) {
if (file == null) {
// Shows save as window
int status;
status = this.showSaveDialog(parent);
if (status == JFileChooser.CANCEL_OPTION) {
return CANCELLED;
} else if (status == JFileChooser.ERROR_OPTION) {
failureMotivation = "Error selecting output file";
return FAILURE;
}
file = dialog.getSelectedFile();
} else
// Check extension to avoid saving over a converted file
if (!file.getName().endsWith(defaultFilter.getFirstExtension())) {
int resultValue = JOptionPane.showConfirmDialog(parent, "<html>File <font color=#0000ff>" + file.getName() + "</font> does not have \""
+ defaultFilter.getFirstExtension() + "\" extension.<br>Do you want to replace it anyway?</html>", "JMT - Warning",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
if (resultValue != JOptionPane.OK_OPTION) {
return CANCELLED;
}
}
// Now checks to save correct type of model
try {
if (defaultFilter == JMODEL || defaultFilter == JSIM) {
XMLArchiver.saveModel(file, (CommonModel) modelData);
} else if (defaultFilter == JMVA) {
xmlutils.saveXML(((ExactModel) modelData).createDocument(), file);
} else if (defaultFilter == JABA) {
xmlutils.saveXML(((JabaModel) modelData).createDocument(), file);
}
} catch (Exception e) {
failureMotivation = e.getClass().getName() + ": " + e.getMessage();
return FAILURE;
}
return SUCCESS;
}
// --------------------------------------------------------------------------------------------
// --- Methods to open and parse files --------------------------------------------------------
protected int getXmlFileType(String fileName) {
// Opens without validating (as we don't know document type)
try {
Document doc = XMLReader.loadXML(fileName);
String root = doc.getDocumentElement().getNodeName();
// Uses root name to determine document type
if (root.equals(XMLConstantNames.XML_DOCUMENT_ROOT)) {
return XML_SIM;
} else if (root.equals(GuiXMLConstants.XML_ARCHIVE_DOCUMENT_ROOT)) {
return XML_ARCHIVE;
} else if (root.equals(ModelLoader.XML_MVA_ROOT)) {
// Finds if this is JMVA or JABA model
String jaba = doc.getDocumentElement().getAttribute("jaba");
if (jaba != null && jaba.equals("true")) {
return XML_JABA;
} else {
return XML_MVA;
}
} else if (root.equals(XMLResultsConstants.XML_DOCUMENT_O_ROOT)) {
return XML_RES_SIM;
} else if (root.equals(XMLResultsConstants.XML_DOCUMENT_ROOT)) {
return XML_RES_GUI;
} else {
return FILE_UNKNOWN;
}
} catch (Exception e) {
// If an exception is thrown, reports that format is unknown
return FILE_UNKNOWN;
}
}
// --------------------------------------------------------------------------------------------
// --- Methods to show dialogs ----------------------------------------------------------------
/**
* Adds all filters to current dialog
*/
protected void addAllFilters() {
dialog.setAcceptAllFileFilterUsed(true);
dialog.addChoosableFileFilter(ALL);
dialog.addChoosableFileFilter(JMODEL);
dialog.addChoosableFileFilter(JSIM);
dialog.addChoosableFileFilter(JMVA);
dialog.addChoosableFileFilter(JABA);
dialog.addChoosableFileFilter(XML);
//dialog.setFileFilter(defaultFilter);
dialog.setFileFilter(ALL);
}
/**
* Adds only compatible filters to current dialog
*/
protected void addCompatibleFilters() {
dialog.setAcceptAllFileFilterUsed(true);
dialog.addChoosableFileFilter(ALL);
if (defaultFilter == JABA) {
dialog.addChoosableFileFilter(JABA);
dialog.setFileFilter(JABA);
} else {
dialog.addChoosableFileFilter(ALL_NOTJABA);
dialog.addChoosableFileFilter(JMODEL);
dialog.addChoosableFileFilter(JSIM);
dialog.addChoosableFileFilter(JMVA);
dialog.setFileFilter(ALL_NOTJABA);
}
}
/**
* Shows open file dialog
* @param parent parent component for this dialog
* @return the return state of the file chooser on popdown:
* <ul>
* <li>JFileChooser.CANCEL_OPTION
* <li>JFileChooser.APPROVE_OPTION
* <li>JFileCHooser.ERROR_OPTION if an error occurs or the
* dialog is dismissed
* </ul>
* @exception HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
*/
protected int showOpenDialog(Component parent) {
addCompatibleFilters();
return dialog.showOpenDialog(parent);
}
/**
* Shows save file dialog
* @param parent the parent component of the dialog,
* can be <code>null</code>;
* see <code>showDialog</code> for details
* @return the return state of the file chooser on popdown:
* <ul>
* <li>JFileChooser.CANCEL_OPTION
* <li>JFileChooser.APPROVE_OPTION
* <li>JFileCHooser.ERROR_OPTION if an error occurs or the
* dialog is dismissed
* </ul>
* @exception HeadlessException if GraphicsEnvironment.isHeadless()
* returns true.
* @see java.awt.GraphicsEnvironment#isHeadless
*/
protected int showSaveDialog(Component parent) {
dialog.resetChoosableFileFilters();
dialog.addChoosableFileFilter(defaultFilter);
dialog.setFileFilter(defaultFilter);
return dialog.showSaveDialog(parent);
}
// --------------------------------------------------------------------------------------------
// --- Inner classes --------------------------------------------------------------------------
/**
* Custom file chooser class
*/
protected static class JmtFileChooser extends JFileChooser {
/**
*
*/
private static final long serialVersionUID = 1L;
protected JmtFileFilter defaultFilter;
/**
* Creates a File chooser in the appropriate directory user deafault.
* @param defaultFilter default file filter
*/
public JmtFileChooser(JmtFileFilter defaultFilter) {
super(Defaults.getWorkingPath());
this.defaultFilter = defaultFilter;
}
/**
* Overrides default method to provide a warning if saving over an existing file
*/
@Override
public void approveSelection() {
// Gets the choosed file name
String name = getSelectedFile().getName();
String parent = getSelectedFile().getParent();
if (getDialogType() == OPEN_DIALOG) {
super.approveSelection();
}
if (getDialogType() == SAVE_DIALOG) {
if (!name.toLowerCase().endsWith(defaultFilter.getFirstExtension())) {
name = name + defaultFilter.getFirstExtension();
setSelectedFile(new File(parent, name));
}
if (getSelectedFile().exists()) {
int resultValue = JOptionPane.showConfirmDialog(this, "<html>File <font color=#0000ff>" + name
+ "</font> already exists in this folder.<br>Do you want to replace it?</html>", "JMT - Warning",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
if (resultValue == JOptionPane.OK_OPTION) {
getSelectedFile().delete();
super.approveSelection();
}
} else {
super.approveSelection();
}
}
}
}
/**
* Inner class used to create simple file filters with only extension check
*/
protected static class JmtFileFilter extends FileFilter {
public static final String SEP = ";";
private String description;
private String[] extensions;
/**
* Creates a new filefilter with specified dot and comma separated list of
* extensions and specified description
* @param extensionList comma separated list of extensions
* of this filter (for example ".jmt;.jmodel")
* @param description description of this filter
*/
public JmtFileFilter(String extensionList, String description) {
this.extensions = extensionList.split(SEP);
this.description = createDescription(description, extensions);
}
/**
* Whether the given file is accepted by this filter.
*/
@Override
public boolean accept(File f) {
String name = f.getName().toLowerCase();
if (f.isDirectory()) {
return true;
}
for (String extension : extensions) {
if (name.endsWith(extension)) {
return true;
}
}
return false;
}
/**
* Creates a suitable description for this filter
* @param text text description of the filter
* @param extensions extensions to be matched
* @return created description
*/
private String createDescription(String text, String[] extensions) {
StringBuffer sb = new StringBuffer();
sb.append(text);
sb.append(" (*");
for (int i = 0; i < extensions.length - 1; i++) {
sb.append(extensions[i]);
sb.append("; *");
}
sb.append(extensions[extensions.length - 1]);
sb.append(")");
return sb.toString();
}
/**
* The description of this filter
* @see javax.swing.filechooser.FileView#getName
*/
@Override
public String getDescription() {
return description;
}
/**
* Gets the first extension of this filter
* @return extension of this filter
*/
public String getFirstExtension() {
return extensions[0];
}
}
// --------------------------------------------------------------------------------------------
}