blob: 3c1a36c9312147e2511ee463245aa3dfcdb577a1 [file] [log] [blame]
package net.sf.taverna.t2.ui.perspectives.myexperiment.model;
// Copyright (C) 2008 The University of Manchester, University of Southampton
// and Cardiff University
import java.awt.BorderLayout;
import java.awt.Color;
import java.io.Serializable;
import java.net.URI;
import java.util.Date;
import java.util.EventListener;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import net.sf.taverna.t2.ui.perspectives.myexperiment.MainComponent;
import net.sf.taverna.t2.ui.perspectives.myexperiment.MyExperimentPerspective;
import net.sf.taverna.t2.ui.perspectives.myexperiment.ResourceListPanel;
import net.sf.taverna.t2.ui.perspectives.myexperiment.StyledHTMLEditorKit;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
/**
* @author Jiten Bhagat, Sergejs Aleksejevs
*/
public class Resource implements Comparable<Resource>, Serializable {
// CONSTANTS
// (integer resource types)
public static final int UNEXPECTED_TYPE = -1; // erroneous type
public static final int UNKNOWN = 0;
public static final int WORKFLOW = 10;
public static final int FILE = 11;
public static final int PACK = 12;
public static final int PACK_INTERNAL_ITEM = 14;
public static final int PACK_EXTERNAL_ITEM = 15;
public static final int USER = 20;
public static final int GROUP = 21;
public static final int TAG = 30;
public static final int COMMENT = 31;
// (string resource types)
public static final String WORKFLOW_VISIBLE_NAME = "Workflow";
public static final String FILE_VISIBLE_NAME = "File";
public static final String PACK_VISIBLE_NAME = "Pack";
public static final String USER_VISIBLE_NAME = "User";
public static final String GROUP_VISIBLE_NAME = "Group";
public static final String TAG_VISIBLE_NAME = "Tag";
public static final String COMMENT_VISIBLE_NAME = "Comment";
public static final String UNKWNOWN_VISIBLE_NAME = "Unknown";
public static final String UNEXPECTED_TYPE_VISIBLE_NAME = "ERROR: Unexpected unknown type!";
// (integer access types)
public static final int ACCESS_VIEWING = 1000;
public static final int ACCESS_DOWNLOADING = 1001;
public static final int ACCESS_EDITING = 1002;
// (categories for selecting required elements for every resource type for a particular purpose)
public static final int REQUEST_ALL_DATA = 5000; // essentially obtains all data that API provides
public static final int REQUEST_FULL_PREVIEW = 5005; // used to get all data for preview in a browser window
public static final int REQUEST_FULL_LISTING = 5010; // used for displaying results of searches by query / by tag
public static final int REQUEST_SHORT_LISTING = 5015; // used for displaying items in 'My Stuff' tab
public static final int REQUEST_USER_FAVOURITES_ONLY = 5050;
public static final int REQUEST_USER_APPLIED_TAGS_ONLY = 5051;
public static final int REQUEST_WORKFLOW_CONTENT_ONLY = 5055;
public static final int REQUEST_DEFAULT_FROM_API = 5100; // used when default fields that come from the API are acceptable
// instance variables
private int iID;
private String uri;
private String resource;
private String title;
private int itemType;
private Date createdAt;
private Date updatedAt;
private String description;
public Resource() {
// empty constructor
}
public int getID() {
return iID;
}
public void setID(int id) {
this.iID = id;
}
public void setID(String id) {
this.iID = Integer.parseInt(id);
}
public String getURI() {
return uri;
}
public String getResource() {
return resource;
}
public int getItemType() {
return itemType;
}
public String getItemTypeName() {
return Resource.getResourceTypeName(itemType);
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setURI(String uri) {
this.uri = uri;
}
public void setResource(String resource) {
this.resource = resource;
}
public void setItemType(int type) {
this.itemType = type;
}
public void setItemType(String type) {
this.itemType = Resource.getResourceTypeFromVisibleName(type);
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public String toString() {
return ("(" + this.getItemTypeName() + ", " + this.getURI() + ","
+ this.getTitle() + ")");
}
/**
* This method is needed to sort Resource instances.
*/
public int compareTo(Resource other) {
int iTypesCompared = this.getItemType() - other.getItemType();
if (iTypesCompared == 0) {
// types are identical, compare by title
return (this.getTitle().compareTo(other.getTitle()));
} else {
// types are different - this is sufficient to order these two resources
// (NB! This presumes that type constants were set in a way that produces correct
// ordering of the types for sorting operations!)
return (iTypesCompared);
}
}
/**
* This makes sure that things like instanceOf() and remove() in List
* interface work properly - this way resources are treated to be the same if
* they store identical data, rather than they simply hold the same reference.
*/
@Override
public boolean equals(Object other) {
// could only be equal to another Resource object, not anything else
if (!(other instanceof Resource))
return (false);
// 'other' object is a Resource; equality is based on the data stored
// in the current and 'other' Resource instances - the main data of the
// Resource: item type, URI in the API and resource URL on myExperiment
// (these fields will always be present in every Resource instance)
Resource otherRes = (Resource) other;
return (this.itemType == otherRes.itemType && this.uri.equals(otherRes.uri) && this.resource.equals(otherRes.resource));
}
/**
* Check if the current type of resource is supposed to have an uploader.
*/
public boolean hasUploader() {
return (this.itemType == Resource.WORKFLOW || this.itemType == Resource.FILE);
}
/**
* Casts the resource to one of the specialist types to get the uploader.
*/
public User getUploader() {
switch (this.itemType) {
case Resource.WORKFLOW:
return ((Workflow) this).getUploader();
case Resource.FILE:
return ((File) this).getUploader();
default:
return (null);
}
}
/**
* Check if the current type of resource is supposed to have a creator.
*/
public boolean hasCreator() {
return (this.itemType == Resource.PACK);
}
/**
* Casts the resource to one of the specialist types to get the creator.
*/
public User getCreator() {
switch (this.itemType) {
case Resource.PACK:
return ((Pack) this).getCreator();
default:
return (null);
}
}
/**
* Check if the current type of resource is supposed to have a administrator.
*/
public boolean hasAdmin() {
return (this.itemType == Resource.GROUP);
}
/**
* Casts the resource to one of the specialist types to get the administrator.
*/
public User getAdmin() {
switch (this.itemType) {
case Resource.GROUP:
return ((Group) this).getAdmin();
default:
return (null);
}
}
/**
* Determines whether the current type of resource can be favourited.
*/
public boolean isFavouritable() {
switch (this.itemType) {
case Resource.WORKFLOW:
case Resource.FILE:
case Resource.PACK:
return (true);
default:
return (false);
}
}
/**
* Determines whether the current resource is favourited by specified user.
*/
public boolean isFavouritedBy(User user) {
for (Resource r : user.getFavourites()) {
if (r.getURI().equals(this.getURI())) {
return (true);
}
}
return (false);
}
/**
* Determines whether the current type of resource can be commented on.
*/
public boolean isCommentableOn() {
switch (this.itemType) {
case Resource.WORKFLOW:
case Resource.FILE:
case Resource.PACK:
case Resource.GROUP:
return (true);
default:
return (false);
}
}
/**
* Retrieves the collection of comments for the current resource.
*/
public List<Comment> getComments() {
switch (this.itemType) {
case Resource.WORKFLOW:
return ((Workflow) this).getComments();
case Resource.FILE:
return ((File) this).getComments();
case Resource.PACK:
return ((Pack) this).getComments();
case Resource.GROUP:
return ((Group) this).getComments();
default:
return (null);
}
}
/**
* Determines whether the current type of resource can be downloaded in
* general.
*/
public boolean isDownloadable() {
return (this.itemType == Resource.WORKFLOW
|| this.itemType == Resource.FILE || this.itemType == Resource.PACK);
}
/**
* Determines whether the current resource instance can be downloaded by the
* current user.
*/
public boolean isDownloadAllowed() {
int iAccessType = 0;
switch (this.itemType) {
case Resource.WORKFLOW:
iAccessType = ((Workflow) this).getAccessType();
break;
case Resource.FILE:
iAccessType = ((File) this).getAccessType();
break;
case Resource.PACK:
iAccessType = ((Pack) this).getAccessType();
break;
default:
iAccessType = 0;
}
return (iAccessType >= Resource.ACCESS_DOWNLOADING);
}
/**
* Only workflows (and files?) have visible types.
*/
public boolean hasVisibleType() {
return (this.itemType == Resource.WORKFLOW || this.itemType == Resource.FILE);
}
public String getVisibleType() {
switch (this.itemType) {
case Resource.WORKFLOW:
return ((Workflow) this).getVisibleType();
case Resource.FILE:
return ((File) this).getVisibleType();
default:
return (null);
}
}
/**
* Will create a small preview panel for the instance of the resource that it
* holds. Shows reduced amount of information when "bCreateFullSizeView" is
* set to false.
*
* @return JPanel containing title, description and links to actions on this
* resource.
*/
public JPanel createListViewPanel(boolean bCreateFullSizeView, MainComponent pluginMainComponent, EventListener eventHandler, Logger logger) {
try {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
JTextPane infoTextPane = new JTextPane();
infoTextPane.setBorder(BorderFactory.createEmptyBorder());
infoTextPane.setEditable(false);
StringBuffer content = new StringBuffer();
content.append("<div class='list_item_container'>");
content.append("<div class='list_item'>");
content.append("<p class='title'>");
content.append("<a href='preview:"
+ this.getItemType()
+ ":"
+ this.getURI()
+ "'>"
+ this.getTitle()
+ ((this.getItemType() == Resource.WORKFLOW) ? (" (version "
+ ((Workflow) this).getVersion() + ")") : "") + "</a>");
content.append("</p>");
if (bCreateFullSizeView) {
// Uploader / Creator / Administrator
if (this.hasUploader()) {
content.append("<p class='uploader'>");
content.append("Uploader: <a href='preview:" + Resource.USER + ":"
+ this.getUploader().getURI() + "'>"
+ this.getUploader().getName() + "</a>");
content.append("</p>");
} else if (this.hasCreator()) {
content.append("<p class='uploader'>");
content.append("Creator: <a href='preview:" + Resource.USER + ":"
+ this.getCreator().getURI() + "'>" + this.getCreator().getName()
+ "</a>");
content.append("</p>");
} else if (this.hasAdmin()) {
content.append("<p class='uploader'>");
content.append("Administrator: <a href='preview:" + Resource.USER
+ ":" + this.getAdmin().getURI() + "'>"
+ this.getAdmin().getName() + "</a>");
content.append("</p>");
}
// Type
if (this.hasVisibleType()) {
content.append("<p class='uploader'>");
content.append("Type: " + this.getVisibleType());
content.append("</p>");
}
}
content.append("<div class='desc'>");
content.append("<table style='margin-top: 5px; margin-bottom: 5px;'>");
content.append("<tr>");
if (this.itemType == Resource.WORKFLOW || this.itemType == Resource.USER) {
boolean bManualResizeNeeded = false; // manual resize will be needed for user avatars (these don't have auto thumbnails)
URI previewURI = null;
if (this.itemType == Resource.WORKFLOW)
previewURI = ((Workflow) this).getThumbnail();
else if (this.itemType == Resource.USER
&& ((User) this).getAvatarResource() != null) {
previewURI = new URI(((User) this).getAvatarResource());
bManualResizeNeeded = true;
}
// preview pictures are only shown for workflows
content.append("<td valign='top'>");
content.append("<a href='preview:" + this.itemType + ":"
+ this.getURI() + "'>");
if (bCreateFullSizeView) {
if (!bManualResizeNeeded)
content.append("<img class='preview' src='" + previewURI
+ "'></img>");
else {
String resizedImageURL = Util.getResizedImageIconTempFileURL(previewURI.toURL(), ResourceListPanel.THUMBNAIL_WIDTH_FOR_FULL_LIST_VIEW, ResourceListPanel.THUMBNAIL_HEIGHT_FOR_FULL_LIST_VIEW);
content.append("<img class='preview' src='" + resizedImageURL
+ "'></img>");
}
} else {
String resizedImageURL = Util.getResizedImageIconTempFileURL(previewURI.toURL(), ResourceListPanel.THUMBNAIL_WIDTH_FOR_SHORT_LIST_VIEW, ResourceListPanel.THUMBNAIL_HEIGHT_FOR_SHORT_LIST_VIEW);
content.append("<img class='preview' src='" + resizedImageURL
+ "'></img>");
}
content.append("</a>");
content.append("</td>");
}
content.append("<td>");
content.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
content.append("</td>");
content.append("<td valign='top' style='margin-bottom: 10px;'>");
if (this.getDescription() != null && this.getDescription().length() > 0) {
if (bCreateFullSizeView)
content.append(this.getDescription());
else {
String strTruncatedDescription = this.getDescription();
if (strTruncatedDescription.length() > ResourceListPanel.DESCRIPTION_TRUNCATE_LENGTH_FOR_SHORT_LIST_VIEW) {
strTruncatedDescription = strTruncatedDescription.substring(0, ResourceListPanel.DESCRIPTION_TRUNCATE_LENGTH_FOR_SHORT_LIST_VIEW);
strTruncatedDescription += " ...";
}
content.append(strTruncatedDescription);
}
} else {
content.append("<span class='none_text'>No description</span>");
}
content.append("</td>");
content.append("</tr>");
content.append("</table>");
content.append("</div>");
if (bCreateFullSizeView) {
content.append("<p style='text-align: left;'><b><a href='"
+ this.getResource()
+ "'>Open in myExperiment</a></b>"
+ "&nbsp;<img style='border: 0px;' src='"
+ MyExperimentPerspective.getLocalResourceURL("external_link_small_icon")
+ "' /></p>");
}
content.append("</div>");
content.append("</div>");
HTMLEditorKit kit = new StyledHTMLEditorKit(pluginMainComponent.getStyleSheet());
HTMLDocument doc = (HTMLDocument) (kit.createDefaultDocument());
doc.insertAfterStart(doc.getRootElements()[0].getElement(0), content.toString());
infoTextPane.setEditorKit(kit);
infoTextPane.setDocument(doc);
infoTextPane.setContentType("text/html");
infoTextPane.addHyperlinkListener((HyperlinkListener) eventHandler);
infoTextPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 7, 0)); // little bit of padding below the HTML preview pane
mainPanel.add(infoTextPane, BorderLayout.CENTER);
if (bCreateFullSizeView) {
JPanel jpButtonsPanel = new JPanel();
jpButtonsPanel.setLayout(new BoxLayout(jpButtonsPanel, BoxLayout.LINE_AXIS));
// "Preview" button
JButton previewButton = new JButton();
previewButton.setAction(pluginMainComponent.new PreviewResourceAction(this.getItemType(), this.getURI()));
jpButtonsPanel.add(previewButton);
// "Download" button
if (this.isDownloadable()) {
// will have a link to the actual resource on myExperiment
// (tests of different conditions are made inside the action)
JButton downloadButton = new JButton();
downloadButton.setAction(pluginMainComponent.new DownloadResourceAction(this));
jpButtonsPanel.add(downloadButton);
}
// "Load" button is only to be displayed for workflows
if (this.getItemType() == Resource.WORKFLOW) {
// (various checks apply to see if this can be done - these are made inside the action)
JButton loadButton = new JButton();
loadButton.setAction(pluginMainComponent.new LoadResourceInTavernaAction(this));
jpButtonsPanel.add(loadButton);
}
// "Import" button is only to be displayed for workflows
if (this.getItemType() == Resource.WORKFLOW) {
// (various checks apply to see if this can be done - these are made inside the action)
JButton importButton = new JButton();
importButton.setAction(pluginMainComponent.new ImportIntoTavernaAction(this));
jpButtonsPanel.add(importButton);
}
// setting look and feel for buttons
mainPanel.add(jpButtonsPanel, BorderLayout.SOUTH);
jpButtonsPanel.setBackground(new Color(247, 247, 247)); // grey background (lighter than main background of the frame)
jpButtonsPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(new Color(235, 235, 235)), // subtle darker border around the button bar
BorderFactory.createEmptyBorder(3, 3, 3, 3) // a bit of padding around the buttons
));
}
// setting border around one workflow entry
if (bCreateFullSizeView) {
mainPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5), BorderFactory.createLineBorder(Color.GRAY)));
} else {
mainPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
}
return (mainPanel);
} catch (Exception e) {
logger.error("Failed while creating " + this.getItemTypeName()
+ " list view:\n" + e);
return (null);
}
}
/**
* Translates resource type codes into a textual representation.
*
* @param resourceTypeCode
* This code should be one of the resource type constants defined in
* Resource class.
* @return Textual translation of the resource type code.
*/
public static String getResourceTypeName(int resourceTypeCode) {
switch (resourceTypeCode) {
case Resource.WORKFLOW:
return Resource.WORKFLOW_VISIBLE_NAME;
case Resource.FILE:
return Resource.FILE_VISIBLE_NAME;
case Resource.PACK:
return Resource.PACK_VISIBLE_NAME;
case Resource.USER:
return Resource.USER_VISIBLE_NAME;
case Resource.GROUP:
return Resource.GROUP_VISIBLE_NAME;
case Resource.TAG:
return Resource.TAG_VISIBLE_NAME;
case Resource.COMMENT:
return Resource.COMMENT_VISIBLE_NAME;
case Resource.UNKNOWN:
return Resource.UNKWNOWN_VISIBLE_NAME;
default:
return Resource.UNEXPECTED_TYPE_VISIBLE_NAME;
}
}
/**
* Translates resource visible name into the type codes.
*/
public static int getResourceTypeFromVisibleName(String name) {
if (name.toLowerCase().equals(Resource.WORKFLOW_VISIBLE_NAME.toLowerCase()))
return (Resource.WORKFLOW);
else if (name.toLowerCase().equals(Resource.FILE_VISIBLE_NAME.toLowerCase()))
return (Resource.FILE);
else if (name.toLowerCase().equals(Resource.PACK_VISIBLE_NAME.toLowerCase()))
return (Resource.PACK);
else if (name.toLowerCase().equals(Resource.USER_VISIBLE_NAME.toLowerCase()))
return (Resource.USER);
else if (name.toLowerCase().equals(Resource.GROUP_VISIBLE_NAME.toLowerCase()))
return (Resource.GROUP);
else if (name.toLowerCase().equals(Resource.TAG_VISIBLE_NAME.toLowerCase()))
return (Resource.TAG);
else if (name.toLowerCase().equals(Resource.COMMENT_VISIBLE_NAME.toLowerCase()))
return (Resource.COMMENT);
else
return (Resource.UNKNOWN);
}
/**
* This method will act as dispatcher for local buildFromXML() methods for
* each of individual resource types. This way there's a generic way to turn
* XML content into a Resource instance.
*/
public static Resource buildFromXML(Document resourceXMLDocument, MyExperimentClient client, Logger logger) {
Element root = resourceXMLDocument.getRootElement();
return (Resource.buildFromXML(root, client, logger));
}
/**
* This method will act as dispatcher for local buildFromXML() methods for
* each of individual resource types. This way there's a generic way to turn
* XML content into a Resource instance.
*/
public static Resource buildFromXML(Element docRootElement, MyExperimentClient client, Logger logger) {
Resource res = null;
switch (Resource.getResourceTypeFromVisibleName(docRootElement.getName())) {
case Resource.WORKFLOW:
res = Workflow.buildFromXML(docRootElement, logger);
break;
case Resource.FILE:
res = File.buildFromXML(docRootElement, logger);
break;
case Resource.PACK:
res = Pack.buildFromXML(docRootElement, client, logger);
break;
case Resource.USER:
res = User.buildFromXML(docRootElement, logger);
break;
case Resource.GROUP:
res = Group.buildFromXML(docRootElement, logger);
break;
default:
// do nothing - will return 'null'
break;
}
return (res);
}
}