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; | |
} | |
} |