blob: 76ef759155f93b83061e37ee355718323583d06c [file] [log] [blame]
package net.sf.taverna.t2.workbench.file.impl.menu;
import static java.awt.event.KeyEvent.VK_0;
import static java.awt.event.KeyEvent.VK_R;
import static javax.swing.Action.MNEMONIC_KEY;
import static javax.swing.Action.NAME;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static javax.swing.JOptionPane.showMessageDialog;
import static javax.swing.SwingUtilities.invokeLater;
import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import net.sf.taverna.t2.lang.observer.Observable;
import net.sf.taverna.t2.lang.observer.Observer;
import net.sf.taverna.t2.ui.menu.AbstractMenuCustom;
import net.sf.taverna.t2.workbench.file.FileManager;
import net.sf.taverna.t2.workbench.file.FileType;
import net.sf.taverna.t2.workbench.file.events.AbstractDataflowEvent;
import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent;
import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent;
import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import uk.org.taverna.configuration.app.ApplicationConfiguration;
import uk.org.taverna.scufl2.api.container.WorkflowBundle;
public class FileOpenRecentMenuAction extends AbstractMenuCustom implements
Observer<FileManagerEvent> {
public static final URI RECENT_URI = URI
.create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenRecent");
private static final String CONF = "conf";
private static Logger logger = Logger
.getLogger(FileOpenRecentMenuAction.class);
private static final String RECENT_WORKFLOWS_XML = "recentWorkflows.xml";
private static final int MAX_ITEMS = 10;
private FileManager fileManager;
private ApplicationConfiguration applicationConfiguration;
private JMenu menu;
private List<Recent> recents = new ArrayList<>();
private Thread loadRecentThread;
public FileOpenRecentMenuAction(FileManager fileManager) {
super(FILE_OPEN_SECTION_URI, 30, RECENT_URI);
this.fileManager = fileManager;
fileManager.addObserver(this);
}
@Override
public void notify(Observable<FileManagerEvent> sender,
FileManagerEvent message) throws Exception {
FileManager fileManager = (FileManager) sender;
if (message instanceof OpenedDataflowEvent
|| message instanceof SavedDataflowEvent) {
AbstractDataflowEvent dataflowEvent = (AbstractDataflowEvent) message;
WorkflowBundle dataflow = dataflowEvent.getDataflow();
Object dataflowSource = fileManager.getDataflowSource(dataflow);
FileType dataflowType = fileManager.getDataflowType(dataflow);
addRecent(dataflowSource, dataflowType);
}
if (message instanceof ClosedDataflowEvent)
// Make sure enabled/disabled status is correct
updateRecentMenu();
}
public void updateRecentMenu() {
invokeLater(new Runnable() {
@Override
public void run() {
updateRecentMenuGUI();
}
});
saveRecent();
}
protected void addRecent(Object dataflowSource, FileType dataflowType) {
if (dataflowSource == null)
return;
if (!(dataflowSource instanceof Serializable)) {
logger.warn("Can't serialize workflow source for 'Recent workflows': "
+ dataflowSource);
return;
}
synchronized (recents) {
Recent recent = new Recent((Serializable) dataflowSource, dataflowType);
if (recents.contains(recent))
recents.remove(recent);
recents.add(0, recent); // Add to front
}
updateRecentMenu();
}
@Override
protected Component createCustomComponent() {
action = new DummyAction("Recent workflows");
action.putValue(MNEMONIC_KEY, VK_R);
menu = new JMenu(action);
// Disabled until we have loaded the recent workflows
menu.setEnabled(false);
loadRecentThread = new Thread("Loading recent workflow menu") {
// Avoid hanging GUI initialization while deserialising
@Override
public void run() {
loadRecent();
updateRecentMenu();
}
};
loadRecentThread.start();
return menu;
}
protected synchronized void loadRecent() {
File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF);
confDir.mkdir();
File recentFile = new File(confDir, RECENT_WORKFLOWS_XML);
if (!recentFile.isFile())
return;
try {
loadRecent(recentFile);
} catch (JDOMException|IOException e) {
logger.warn("Could not read recent workflows from file "
+ recentFile, e);
}
}
private void loadRecent(File recentFile) throws FileNotFoundException,
IOException, JDOMException {
SAXBuilder builder = new SAXBuilder();
@SuppressWarnings("unused")
Document document;
try (InputStream fileInputStream = new BufferedInputStream(
new FileInputStream(recentFile))) {
document = builder.build(fileInputStream);
}
synchronized (recents) {
recents.clear();
//RecentDeserializer deserialiser = new RecentDeserializer();
try {
// recents.addAll(deserialiser.deserializeRecent(document
// .getRootElement()));
} catch (Exception e) {
logger.warn("Could not read recent workflows from file "
+ recentFile, e);
}
}
}
protected synchronized void saveRecent() {
File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF);
confDir.mkdir();
File recentFile = new File(confDir, RECENT_WORKFLOWS_XML);
try {
saveRecent(recentFile);
// } catch (JDOMException e) {
// logger.warn("Could not generate XML for recent workflows to file "
// + recentFile, e);
} catch (IOException e) {
logger.warn("Could not write recent workflows to file "
+ recentFile, e);
}
}
private void saveRecent(File recentFile) throws FileNotFoundException,
IOException {
// RecentSerializer serializer = new RecentSerializer();
// XMLOutputter outputter = new XMLOutputter();
// Element serializedRecent;
synchronized (recents) {
if (recents.size() > MAX_ITEMS)
// Remove excess entries
recents.subList(MAX_ITEMS, recents.size()).clear();
// serializedRecent = serializer.serializeRecent(recents);
}
try (OutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(recentFile))) {
// outputter.output(serializedRecent, outputStream);
}
}
protected void updateRecentMenuGUI() {
int items = 0;
menu.removeAll();
synchronized (recents) {
for (Recent recent : recents) {
if (++items >= MAX_ITEMS)
break;
OpenRecentAction openRecentAction = new OpenRecentAction(
recent, fileManager);
if (fileManager.getDataflowBySource(recent.getDataflowSource()) != null)
openRecentAction.setEnabled(false);
// else setEnabled(true)
JMenuItem menuItem = new JMenuItem(openRecentAction);
if (items < 10) {
openRecentAction.putValue(NAME, items + " "
+ openRecentAction.getValue(NAME));
menuItem.setMnemonic(VK_0 + items);
}
menu.add(menuItem);
}
}
menu.setEnabled(items > 0);
menu.revalidate();
}
@SuppressWarnings("serial")
public static class OpenRecentAction extends AbstractAction implements
Runnable {
private final Recent recent;
private Component component = null;
private final FileManager fileManager;
public OpenRecentAction(Recent recent, FileManager fileManager) {
this.recent = recent;
this.fileManager = fileManager;
Serializable source = recent.getDataflowSource();
String name;
if (source instanceof File)
name = ((File) source).getAbsolutePath();
else
name = source.toString();
this.putValue(NAME, name);
this.putValue(SHORT_DESCRIPTION, "Open the workflow " + name);
}
@Override
public void actionPerformed(ActionEvent e) {
component = null;
if (e.getSource() instanceof Component)
component = (Component) e.getSource();
setEnabled(false);
new Thread(this, "Opening workflow from "
+ recent.getDataflowSource()).start();
}
/**
* Opening workflow in separate thread
*/
@Override
public void run() {
final Serializable source = recent.getDataflowSource();
try {
fileManager.openDataflow(recent.makefileType(), source);
} catch (OpenException ex) {
logger.warn("Failed to open the workflow from " + source
+ " \n", ex);
showMessageDialog(component,
"Failed to open the workflow from url " + source
+ " \n" + ex.getMessage(), "Error!",
ERROR_MESSAGE);
} finally {
setEnabled(true);
}
}
}
@SuppressWarnings("serial")
public static class Recent implements Serializable {
private final class RecentFileType extends FileType {
@Override
public String getMimeType() {
return mimeType;
}
@Override
public String getExtension() {
return extension;
}
@Override
public String getDescription() {
return "File type " + extension + " " + mimeType;
}
}
private Serializable dataflowSource;
private String mimeType;
private String extension;
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public String getExtension() {
return extension;
}
public void setExtension(String extension) {
this.extension = extension;
}
public Recent() {
}
public FileType makefileType() {
if (mimeType == null && extension == null)
return null;
return new RecentFileType();
}
public Recent(Serializable dataflowSource, FileType dataflowType) {
setDataflowSource(dataflowSource);
if (dataflowType != null) {
setMimeType(dataflowType.getMimeType());
setExtension(dataflowType.getExtension());
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((dataflowSource == null) ? 0 : dataflowSource.hashCode());
result = prime * result
+ ((extension == null) ? 0 : extension.hashCode());
result = prime * result
+ ((mimeType == null) ? 0 : mimeType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Recent))
return false;
Recent other = (Recent) obj;
if (dataflowSource == null) {
if (other.dataflowSource != null)
return false;
} else if (!dataflowSource.equals(other.dataflowSource))
return false;
if (extension == null) {
if (other.extension != null)
return false;
} else if (!extension.equals(other.extension))
return false;
if (mimeType == null) {
if (other.mimeType != null)
return false;
} else if (!mimeType.equals(other.mimeType))
return false;
return true;
}
public Serializable getDataflowSource() {
return dataflowSource;
}
public void setDataflowSource(Serializable dataflowSource) {
this.dataflowSource = dataflowSource;
}
@Override
public String toString() {
return getDataflowSource() + "";
}
}
// TODO find new serialization
// protected static class RecentDeserializer extends AbstractXMLDeserializer {
// public Collection<Recent> deserializeRecent(Element el) {
// return (Collection<Recent>) super.createBean(el, getClass()
// .getClassLoader());
// }
// }
//
// protected static class RecentSerializer extends AbstractXMLSerializer {
// public Element serializeRecent(List<Recent> x) throws JDOMException,
// IOException {
// Element beanAsElement = super.beanAsElement(x);
// return beanAsElement;
// }
// }
public void setApplicationConfiguration(
ApplicationConfiguration applicationConfiguration) {
this.applicationConfiguration = applicationConfiguration;
}
}