/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 University of Dundee. All rights reserved. * * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.agents.fsimporter.util; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import info.clearthought.layout.TableLayout; import info.clearthought.layout.TableLayoutConstraints; import omero.cmd.CmdCallback; import omero.cmd.CmdCallbackI; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.FileUtils; import org.jdesktop.swingx.JXBusyLabel; import org.jdesktop.swingx.JXTaskPane; import org.openmicroscopy.shoola.agents.events.importer.BrowseContainer; import org.openmicroscopy.shoola.agents.events.iviewer.ViewImage; import org.openmicroscopy.shoola.agents.events.iviewer.ViewImageObject; import org.openmicroscopy.shoola.agents.fsimporter.IconManager; import org.openmicroscopy.shoola.agents.fsimporter.ImporterAgent; import org.openmicroscopy.shoola.agents.util.EditorUtil; import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplay; import org.openmicroscopy.shoola.agents.util.ui.EditorDialog; import org.openmicroscopy.shoola.agents.util.ui.ThumbnailLabel; import org.openmicroscopy.shoola.env.data.ImportException; import org.openmicroscopy.shoola.env.data.model.FileObject; import org.openmicroscopy.shoola.env.data.model.ImportableFile; import org.openmicroscopy.shoola.env.data.model.ThumbnailData; import omero.gateway.SecurityContext; import org.openmicroscopy.shoola.env.data.util.StatusLabel; import org.openmicroscopy.shoola.env.event.EventBus; import org.openmicroscopy.shoola.util.file.ImportErrorObject; import org.openmicroscopy.shoola.util.ui.UIUtilities; import omero.gateway.model.DataObject; import omero.gateway.model.DatasetData; import omero.gateway.model.FileAnnotationData; import omero.gateway.model.FilesetData; import omero.gateway.model.PixelsData; import omero.gateway.model.PlateData; import omero.gateway.model.ProjectData; import omero.gateway.model.ScreenData; import omero.gateway.model.TagAnnotationData; /** * Component hosting the file to import and displaying the status of the * import process. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @author Blazej Pindelski, bpindelski at dundee.ac.uk * @version 3.0 * @since 3.0-Beta4 */ public class FileImportComponent extends JPanel implements PropertyChangeListener { /** Indicates that the container is of type <code>Project</code>. */ public static final int PROJECT_TYPE = 0; /** Indicates that the container is of type <code>Screen</code>. */ public static final int SCREEN_TYPE = 1; /** Indicates that the container is of type <code>Dataset</code>. */ public static final int DATASET_TYPE = 2; /** Indicates that no container specified. */ public static final int NO_CONTAINER = 3; /** Bound property indicating to retry an upload.*/ public static final String RETRY_PROPERTY = "retry"; /** * Bound property indicating that the error to submit is selected or not. */ public static final String SUBMIT_ERROR_PROPERTY = "submitError"; /** Bound property indicating to display the error.*/ public static final String DISPLAY_ERROR_PROPERTY = "displayError"; /** Bound property indicating to cancel the import.*/ public static final String CANCEL_IMPORT_PROPERTY = "cancelImport"; /** Bound property indicating to browse the node. */ public static final String BROWSE_PROPERTY = "browse"; /** Bound property indicating to increase the number of files to import. */ public static final String IMPORT_FILES_NUMBER_PROPERTY = "importFilesNumber"; /** * Bound property indicating to load the content of the log file. */ public static final String LOAD_LOGFILEPROPERTY = "loadLogfile"; /** * Bound property indicating to retrieve the log file. */ public static final String RETRIEVE_LOGFILEPROPERTY = "retrieveLogfile"; /** * Bound property indicating to show the checksums, */ public static final String CHECKSUM_DISPLAY_PROPERTY = "checksumDisplay"; /** The default size of the busy label. */ private static final Dimension SIZE = new Dimension(16, 16); /** The number of extra labels for images to add. */ public static final int MAX_THUMBNAILS = 3; /** Text indicating that the folder does not contain importable files.*/ private static final String EMPTY_FOLDER = "No data to import"; /** The maximum width used for the component.*/ private static final int LENGTH = 350; /** private static final String EMPTY_DIRECTORY = "No data to import"; /** One of the constants defined by this class. */ private int type; /** The component indicating the progress of the import. */ private JXBusyLabel busyLabel; /** The component displaying the file name. */ private JPanel namePane; /** The component displaying the result. */ private JLabel resultLabel; /** The component displaying the imported image. */ private ThumbnailLabel imageLabel; /** Keeps track of the extra images if any. */ private List<ThumbnailLabel> imageLabels; /** The imported image. */ private Object image; /** Indicates the status of the on-going import. */ private StatusLabel statusLabel; /** The component displaying the name of the file. */ private JLabel fileNameLabel; /** Keep tracks of the components. */ private Map<File, FileImportComponent> components; /** The mouse adapter to view the image. */ private MouseAdapter adapter; /** The data object corresponding to the folder. */ private DataObject containerFromFolder; /** Button to cancel the import for that file. */ private JButton cancelButton; /** The node where to import the folder. */ private DataObject data; /** The dataset if any. */ private DatasetData dataset; /** The node of reference if any. */ private Object refNode; /** The object where the data have been imported.*/ private DataObject containerObject; /** The component used when importing a folder. */ private JXTaskPane pane; /** The parent of the node. */ private FileImportComponent parent; /** * Flag indicating that the container hosting the imported image * can be browsed or not depending on how the import is launched. */ private boolean browsable; /** Set to <code>true</code> if attempt to re-import.*/ private boolean reimported; /** Flag indicating the the user is member of one group only.*/ private boolean singleGroup; /** The button displayed the various options post if the import worked.*/ private JButton actionMenuButton; /** The popup menu associated with the action button */ private JPopupMenu menu; /** The state of the import */ private ImportStatus resultIndex; /** The index associated to the main component.*/ private int index; /** Reference to the callback.*/ private CmdCallback callback; /** The importable object.*/ private ImportableFile importable; /** The collection of tags added to the imported images.*/ private Collection<TagAnnotationData> tags; /** The label indicating the status of the import.*/ private JLabel refLabel; /** The button displaying the available action.*/ private JButton refButton; /** Retries to upload the file.*/ private void retry() { Object o = statusLabel.getImportResult(); if (o instanceof Exception || image instanceof Exception) firePropertyChange(RETRY_PROPERTY, null, this); } /** * Creates or recycles the menu corresponding to the import status. * * @return See above. */ private JPopupMenu createActionMenu() { if (menu != null) return menu; menu = new JPopupMenu(); JMenuItem item; String logText = "View Import Log"; String checksumText = "View Checksum"; String exceptionText = "View Exception"; String copyExceptionText = "Copy Exception to Clipboard"; Object result = statusLabel.getImportResult(); switch (resultIndex) { case FAILURE_LIBRARY: menu.add(new JMenuItem(new AbstractAction(exceptionText) { public void actionPerformed(ActionEvent e) { viewError(); } })); menu.add(new JMenuItem(new AbstractAction(copyExceptionText) { public void actionPerformed(ActionEvent e) { copyErrorToClipboard(); } })); break; case FAILURE: menu.add(new JMenuItem(new AbstractAction("Submit") { public void actionPerformed(ActionEvent e) { submitError(); } })); menu.add(new JMenuItem(new AbstractAction(exceptionText) { public void actionPerformed(ActionEvent e) { viewError(); } })); menu.add(new JMenuItem(new AbstractAction(copyExceptionText) { public void actionPerformed(ActionEvent e) { copyErrorToClipboard(); } })); break; case UPLOAD_FAILURE: menu.add(new JMenuItem(new AbstractAction("Retry") { public void actionPerformed(ActionEvent e) { retry(); } })); break; case SUCCESS: logText = "Import Log"; checksumText = "Checksum"; item = new JMenuItem(new AbstractAction("In Full Viewer") { public void actionPerformed(ActionEvent e) { launchFullViewer(); } }); boolean b = false; if (result instanceof Collection) b = ((Collection) result).size() == 1; item.setEnabled(b && !statusLabel.isHCS()); menu.add(item); item = new JMenuItem(new AbstractAction("In Data Browser") { public void actionPerformed(ActionEvent e) { browse(); } }); item.setEnabled(browsable); menu.add(item); } item = new JMenuItem(new AbstractAction(logText) { public void actionPerformed(ActionEvent e) { displayLogFile(); } }); item.setEnabled(statusLabel.getLogFileID() > 0); menu.add(item); item = new JMenuItem(new AbstractAction(checksumText) { public void actionPerformed(ActionEvent e) { showChecksumDetails(); } }); item.setEnabled(statusLabel.hasChecksum()); menu.add(item); return menu; } /** Displays or loads the log file.*/ private void displayLogFile() { firePropertyChange(LOAD_LOGFILEPROPERTY, null, this); } /** * Displays the checksum details dialog for the file(s) in this entry */ private void showChecksumDetails() { firePropertyChange(CHECKSUM_DISPLAY_PROPERTY, null, statusLabel); } /** * Formats the tool tip of a successful import. * * @return See above. */ private void formatResultTooltip() { StringBuffer buf = new StringBuffer(); buf.append("<html><body>"); buf.append("<b>Date Uploaded: </b>"); buf.append(UIUtilities.formatShortDateTime(null)); buf.append("<br>"); if (image instanceof PlateData) { PlateData p = (PlateData) image; buf.append("<b>Plate ID: </b>"); buf.append(p.getId()); buf.append("<br>"); } if (!statusLabel.isHCS()) { Object o = statusLabel.getImportResult(); if (o instanceof Set) { Set<PixelsData> list = (Set<PixelsData>) o; int n = list.size(); if (n == 1) { buf.append("<b>Image ID: </b>"); Iterator<PixelsData> i = list.iterator(); while (i.hasNext()) { buf.append(i.next().getImage().getId()); buf.append("<br>"); } } else if (n > 1) { buf.append("<b>Number of Images: </b>"); buf.append(n); buf.append("<br>"); } } } buf.append("<b>Size: </b>"); buf.append(FileUtils.byteCountToDisplaySize(statusLabel.getFileSize())); buf.append("<br>"); buf.append("<b>Group: </b>"); buf.append(importable.getGroup().getName()); buf.append("<br>"); buf.append("<b>Owner: </b>"); buf.append(EditorUtil.formatExperimenter(importable.getUser())); buf.append("<br>"); if (containerObject instanceof ProjectData) { buf.append("<b>Project: </b>"); buf.append(((ProjectData) containerObject).getName()); buf.append("<br>"); } else if (containerObject instanceof ScreenData) { buf.append("<b>Screen: </b>"); buf.append(((ScreenData) containerObject).getName()); buf.append("<br>"); } else if (containerObject instanceof DatasetData) { buf.append("<b>Dataset: </b>"); buf.append(((DatasetData) containerObject).getName()); buf.append("<br>"); } else if (dataset != null) { buf.append("<b>Dataset: </b>"); buf.append(dataset.getName()); buf.append("<br>"); } if (!CollectionUtils.isEmpty(tags)) { buf.append("<b>Tags: </b>"); Iterator<TagAnnotationData> i = tags.iterator(); while (i.hasNext()) { buf.append(i.next().getTagValue()); buf.append(" "); } } buf.append("</body></html>"); String tip = buf.toString(); fileNameLabel.setToolTipText(tip); resultLabel.setToolTipText(tip); } /** Indicates that the import was successful or if it failed.*/ private void formatResult() { if (callback != null) { try { ((CmdCallbackI) callback).close(true); } catch (Exception e) {} } if (namePane.getPreferredSize().width > LENGTH) fileNameLabel.setText(EditorUtil.getPartialName( getFile().getName())); resultLabel.setVisible(true); busyLabel.setVisible(false); busyLabel.setBusy(false); remove(refLabel); remove(refButton); refLabel = resultLabel; refButton = actionMenuButton; addControlsToDisplay(); IconManager icons = IconManager.getInstance(); Object result = statusLabel.getImportResult(); if (image instanceof ImportException) result = image; if (result instanceof ImportException) { ImportException e = (ImportException) result; resultLabel.setIcon(icons.getIcon(IconManager.DELETE)); resultLabel.setToolTipText( UIUtilities.formatExceptionForToolTip(e)); actionMenuButton.setVisible(true); actionMenuButton.setForeground(UIUtilities.REQUIRED_FIELDS_COLOR); actionMenuButton.setText("Failed"); int status = e.getStatus(); if (status == ImportException.CHECKSUM_MISMATCH) resultIndex = ImportStatus.UPLOAD_FAILURE; else if (status == ImportException.MISSING_LIBRARY) resultIndex = ImportStatus.FAILURE_LIBRARY; else resultIndex = ImportStatus.FAILURE; statusLabel.setText(""); } else if (result instanceof CmdCallback) { callback = (CmdCallback) result; } else { formatResultTooltip(); resultLabel.setIcon(icons.getIcon(IconManager.APPLY)); actionMenuButton.setVisible(true); actionMenuButton.setForeground(UIUtilities.HYPERLINK_COLOR); actionMenuButton.setText("View"); resultIndex = ImportStatus.SUCCESS; } } /** Submits the error.*/ private void submitError() { Object o = statusLabel.getImportResult(); if (o instanceof Exception) firePropertyChange(SUBMIT_ERROR_PROPERTY, null, this); } /** Views the error.*/ private void viewError() { Object o = statusLabel.getImportResult(); if (o instanceof ImportException) { String v = UIUtilities.printErrorText((ImportException) o); JFrame f = ImporterAgent.getRegistry().getTaskBar().getFrame(); EditorDialog d = new EditorDialog(f, v, EditorDialog.VIEW_TYPE); d.allowEdit(false); UIUtilities.centerAndShow(d); } } /** Copies the error to the clipboard.*/ private void copyErrorToClipboard() { Object o = statusLabel.getImportResult(); if (o instanceof ImportException) { String v = UIUtilities.printErrorText((ImportException) o); UIUtilities.copyToClipboard(v); } } /** Browses the node or the data object. */ private void browse() { EventBus bus = ImporterAgent.getRegistry().getEventBus(); Object d = dataset; if (dataset == null || data instanceof ScreenData) d = data; if (d == null) return; bus.post(new BrowseContainer(d, null)); } /** * Indicates that the file will not be imported. * * @param fire Pass <code>true</code> to fire a property, * <code>false</code> otherwise. */ private void cancel(boolean fire) { boolean b = statusLabel.isCancellable() || getFile().isDirectory(); if (!isCancelled() && !hasImportFailed() && b && !statusLabel.isMarkedAsDuplicate()) { busyLabel.setBusy(false); busyLabel.setVisible(false); statusLabel.markedAsCancel(); cancelButton.setEnabled(false); cancelButton.setVisible(false); firePropertyChange(CANCEL_IMPORT_PROPERTY, null, this); } } /** * Launches the full viewer for the selected item. */ private void launchFullViewer() { ViewImage evt; int plugin = ImporterAgent.runAsPlugin(); if (image == null) image = statusLabel.getImportResult(); Object ho = image; if (image instanceof Collection) { Collection l = (Collection) image; if (CollectionUtils.isEmpty(l) || l.size() > 1) return; Iterator<Object> i = l.iterator(); while (i.hasNext()) { ho = i.next(); } } if (ho instanceof ThumbnailData) { ThumbnailData data = (ThumbnailData) ho; EventBus bus = ImporterAgent.getRegistry().getEventBus(); evt = new ViewImage( new SecurityContext(importable.getGroup().getId()), new ViewImageObject(data.getImageID()), null); evt.setPlugin(plugin); bus.post(evt); } else if (ho instanceof PixelsData) { PixelsData data = (PixelsData) ho; EventBus bus = ImporterAgent.getRegistry().getEventBus(); evt = new ViewImage( new SecurityContext(importable.getGroup().getId()), new ViewImageObject(data.getImage().getId()), null); evt.setPlugin(plugin); bus.post(evt); } else if (image instanceof PlateData) { firePropertyChange(BROWSE_PROPERTY, null, image); } } /** Initializes the components. */ private void initComponents() { actionMenuButton = new JButton(); actionMenuButton.setVisible(false); actionMenuButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { JPopupMenu popup = createActionMenu(); popup.show(actionMenuButton, 0, actionMenuButton.getHeight()); } }); adapter = new MouseAdapter() { /** * Views the image. * @see MouseListener#mousePressed(MouseEvent) */ public void mousePressed(MouseEvent e) { if (e.getClickCount() == 1) { launchFullViewer(); } } }; setLayout(new FlowLayout(FlowLayout.LEFT)); busyLabel = new JXBusyLabel(SIZE); busyLabel.setVisible(false); busyLabel.setBusy(false); cancelButton = new JButton("Cancel"); cancelButton.setForeground(UIUtilities.HYPERLINK_COLOR); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { cancelLoading(); } }); cancelButton.setVisible(true); namePane = new JPanel(); namePane.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5)); IconManager icons = IconManager.getInstance(); Icon icon; if (getFile().isFile()) icon = icons.getIcon(IconManager.IMAGE); else icon = icons.getIcon(IconManager.DIRECTORY); imageLabel = new ThumbnailLabel(icon); imageLabel.addPropertyChangeListener(this); imageLabels = new ArrayList<ThumbnailLabel>(); ThumbnailLabel label; for (int i = 0; i < MAX_THUMBNAILS; i++) { label = new ThumbnailLabel(); if (i == MAX_THUMBNAILS-1) { Font f = label.getFont(); label.setFont(f.deriveFont(f.getStyle(), f.getSize()-2)); } label.setVisible(false); label.addPropertyChangeListener(this); imageLabels.add(label); } fileNameLabel = new JLabel(getFile().getName()); namePane.add(imageLabel); Iterator<ThumbnailLabel> j = imageLabels.iterator(); while (j.hasNext()) { namePane.add(j.next()); } namePane.add(Box.createHorizontalStrut(4)); namePane.add(fileNameLabel); namePane.add(Box.createHorizontalStrut(10)); resultLabel = new JLabel(); statusLabel = new StatusLabel(importable.getFile()); statusLabel.addPropertyChangeListener(this); image = null; refButton = cancelButton; refLabel = busyLabel; } /** Builds and lays out the UI. */ private void buildGUI() { double[][] design = new double[][] {{LENGTH, TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED, TableLayout.PREFERRED}, {TableLayout.PREFERRED}}; setLayout(new TableLayout(design)); removeAll(); if (namePane.getPreferredSize().width > LENGTH) fileNameLabel.setText(EditorUtil.getPartialName( getFile().getName())); add(UIUtilities.buildComponentPanel(namePane, false), "0, 0, l, c"); add(statusLabel, "1, 0, l, c"); /* add(busyLabel, "2, 0, l, c"); add(resultLabel, "3, 0, l, c"); add(UIUtilities.buildComponentPanel(cancelButton, false), "4, 0, l, c"); add(UIUtilities.buildComponentPanel(actionMenuButton, false), "5, 0, l, c"); */ addControlsToDisplay(); } private void addControlsToDisplay() { add(refLabel, "2, 0, l, c"); add(UIUtilities.buildComponentPanel(refButton, false), "3, 0, l, c"); } /** * Attaches the listeners to the newly created component. * * @param c The component to handle. */ private void attachListeners(FileImportComponent c) { PropertyChangeListener[] listeners = getPropertyChangeListeners(); if (listeners != null && listeners.length > 0) { for (int j = 0; j < listeners.length; j++) { c.addPropertyChangeListener(listeners[j]); } } } /** * Adds the specified files to the list of import data. * * @param files The files to import. */ private void insertFiles(Map<File, StatusLabel> files) { resultIndex = ImportStatus.SUCCESS; if (files == null || files.size() == 0) return; components = new HashMap<File, FileImportComponent>(); Entry<File, StatusLabel> entry; Iterator<Entry<File, StatusLabel>> i = files.entrySet().iterator(); FileImportComponent c; File f; DatasetData d = dataset; Object node = refNode; if (importable.isFolderAsContainer()) { node = null; d = new DatasetData(); d.setName(getFile().getName()); } ImportableFile copy; while (i.hasNext()) { entry = i.next(); f = entry.getKey(); copy = importable.copy(); copy.setFile(f); c = new FileImportComponent(copy, browsable, singleGroup, getIndex(), tags); if (f.isFile()) { c.setLocation(data, d, node); c.setParent(this); } c.setType(getType()); attachListeners(c); c.setStatusLabel(entry.getValue()); entry.getValue().addPropertyChangeListener(this); components.put((File) entry.getKey(), c); } removeAll(); pane = EditorUtil.createTaskPane(getFile().getName()); pane.setCollapsed(false); IconManager icons = IconManager.getInstance(); pane.setIcon(icons.getIcon(IconManager.DIRECTORY)); Font font = pane.getFont(); pane.setFont(font.deriveFont(font.getStyle(), font.getSize()-2)); layoutEntries(false); double[][] size = {{TableLayout.FILL}, {TableLayout.PREFERRED}}; setLayout(new TableLayout(size)); add(pane, new TableLayoutConstraints(0, 0)); validate(); repaint(); } /** * Creates a new instance. * * @param importable The component hosting information about the file. * @param browsable Flag indicating that the container can be browsed or not. * @param singleGroup Passes <code>true</code> if the user is member of * only one group, <code>false</code> otherwise. * @param index The index of the parent. * @param tags The tags that will be linked to the objects. */ public FileImportComponent(ImportableFile importable, boolean browsable, boolean singleGroup, int index, Collection<TagAnnotationData> tags) { if (importable == null) throw new IllegalArgumentException("No file specified."); if (importable.getGroup() == null) throw new IllegalArgumentException("No group specified."); this.index = index; this.tags = tags; this.importable = importable; this.singleGroup = singleGroup; this.browsable = browsable; resultIndex = ImportStatus.QUEUED; initComponents(); buildGUI(); setLocation(importable.getParent(), importable.getDataset(), importable.getRefNode()); } /** * Returns the file hosted by this component. * * @return See above. */ public FileObject getFile() { return importable.getFile(); } /** * Returns the file hosted by this component. * * @return See above. */ public FileObject getOriginalFile() { return importable.getOriginalFile(); } /** * Sets the location where to import the files. * * @param data The data where to import the folder or screening data. * @param dataset The dataset if any. * @param refNode The node of reference. */ public void setLocation(DataObject data, DatasetData dataset, Object refNode) { this.data = data; this.dataset = dataset; this.refNode = refNode; if (refNode != null && refNode instanceof TreeImageDisplay) { TreeImageDisplay n = (TreeImageDisplay) refNode; Object ho = n.getUserObject(); if (ho instanceof DatasetData || ho instanceof ProjectData || ho instanceof ScreenData) { containerObject = (DataObject) ho; } return; } if (dataset != null) { containerObject = dataset; return; } if (data != null && data instanceof ScreenData) { containerObject = data; } } /** * Sets the log file annotation. * * @param data The annotation associated to the file set. * @param id The id of the file set. */ public void setImportLogFile(Collection<FileAnnotationData> data, long id) { } /** * Returns the dataset or <code>null</code>. * * @return See above. */ public DatasetData getDataset() { return dataset; } /** * Returns the object or <code>null</code>. * * @return See above. */ public DataObject getDataObject() { return data; } /** * Replaces the initial status label. * * @param label The value to replace. */ void setStatusLabel(StatusLabel label) { statusLabel = label; statusLabel.addPropertyChangeListener(this); buildGUI(); revalidate(); repaint(); } /** * Sets the parent of the component. * * @param parent The value to set. */ void setParent(FileImportComponent parent) { this.parent = parent; } /** * Returns <code>true</code> if the parent is set. * <code>false</code> otherwise. * * @return See above. */ public boolean hasParent() { return parent != null; } /** * Returns the components displaying the status of an on-going import. * * @return See above. */ public StatusLabel getStatus() { return statusLabel; } /** * Returns the associated file if any. * * @param series See above. * @return See above. */ private FileObject getAssociatedFile(int series) { List<FileObject> l = getFile().getAssociatedFiles(); Iterator<FileObject> i = l.iterator(); FileObject f; while (i.hasNext()) { f = i.next(); if (f.getIndex() == series) { return f; } } return null; } /** * Returns <code>true</code> if the file has some associated files, * <code>false</code> otherwise. * * @return See above. */ private boolean hasAssociatedFiles() { List<FileObject> l = getFile().getAssociatedFiles(); return CollectionUtils.isNotEmpty(l); } /** * Sets the result of the import. * @param image The image. */ public void setStatus(Object image) { busyLabel.setVisible(false); busyLabel.setBusy(false); cancelButton.setVisible(false); this.image = image; if (image instanceof PlateData) { menu = null; imageLabel.setData((PlateData) image); fileNameLabel.addMouseListener(adapter); formatResultTooltip(); } else if (image instanceof Set) { //Result from the import itself this.image = null; Set set = (Set) image; Iterator i = set.iterator(); FileObject f; while (i.hasNext()) { Object object = i.next(); if (object instanceof PixelsData) { PixelsData pix = (PixelsData) object; if (hasAssociatedFiles()) { int series = pix.getImage().getSeries(); f = getAssociatedFile(series); if (f != null) { f.setImageID(pix.getImage().getId()); } } else { f = getOriginalFile(); f.setImageID(pix.getImage().getId()); } } } formatResult(); } else if (image instanceof List) { List<ThumbnailData> list = new ArrayList<ThumbnailData>((List) image); int m = list.size(); ThumbnailData data = list.get(0); imageLabel.setData(data); list.remove(0); if (list.size() > 0) { ThumbnailLabel label = imageLabels.get(0); label.setVisible(true); label.setData(list.get(0)); list.remove(0); if (list.size() > 0) { label = imageLabels.get(1); label.setVisible(true); label.setData(list.get(0)); list.remove(0); int n = statusLabel.getNumberOfImportedFiles()-m; if (n > 0) { label = imageLabels.get(2); label.setVisible(true); StringBuffer buf = new StringBuffer("... "); buf.append(n); buf.append(" more"); label.setText(buf.toString()); } } } } else if (image instanceof ImportException) { if (getFile().isDirectory()) { this.image = null; statusLabel.setText(EMPTY_FOLDER); } else formatResult(); } else if (image instanceof Boolean) { busyLabel.setBusy(false); busyLabel.setVisible(false); cancelButton.setVisible(false); if (statusLabel.isMarkedAsCancel() || statusLabel.isMarkedAsDuplicate()) { resultIndex = ImportStatus.IGNORED; this.image = null; } } revalidate(); repaint(); } /** * Returns the files that failed to import. * * @return See above. */ public List<FileImportComponent> getImportErrors() { List<FileImportComponent> l = null; if (getFile().isFile()) { Object r = statusLabel.getImportResult(); if (r instanceof Exception || image instanceof Exception) { l = new ArrayList<FileImportComponent>(); l.add(this); return l; } } else { if (components != null) { Iterator<FileImportComponent> i = components.values().iterator(); FileImportComponent fc; l = new ArrayList<FileImportComponent>(); List<FileImportComponent> list; while (i.hasNext()) { fc = i.next(); list = fc.getImportErrors(); if (!CollectionUtils.isEmpty(list)) l.addAll(list); } } } return l; } /** * Returns the id of the group. * * @return See above. */ public long getGroupID() { return importable.getGroup().getId(); } /** * Returns the id of the experimenter. * * @return See above. */ public long getExperimenterID() { return importable.getUser().getId(); } /** * Returns the import error object. * * @return See above. */ public ImportErrorObject getImportErrorObject() { Object r = statusLabel.getImportResult(); Exception e = null; if (r instanceof Exception) e = (Exception) r; else if (image instanceof Exception) e = (Exception) image; if (e == null) return null; ImportErrorObject object = new ImportErrorObject( getFile().getTrueFile(), e, getGroupID()); object.setImportContainer(statusLabel.getImportContainer()); long id = statusLabel.getLogFileID(); if (id <= 0) { FilesetData data = statusLabel.getFileset(); if (data != null) { id = data.getId(); object.setRetrieveFromAnnotation(true); } } object.setLogFileID(id); return object; } /** * Returns <code>true</code> if the import has failed, <code>false</code> * otherwise. * * @return See above. */ public boolean hasImportFailed() { return resultIndex == ImportStatus.FAILURE || resultIndex == ImportStatus.UPLOAD_FAILURE; } /** * Returns <code>true</code> if it was a failure prior or during the * upload, <code>false</code> otherwise. * * @return See above. */ public boolean hasUploadFailed() { return resultIndex == ImportStatus.UPLOAD_FAILURE || (resultIndex == ImportStatus.FAILURE && !statusLabel.didUploadStart()); } /** * Returns <code>true</code> if the import has been cancelled, * <code>false</code> otherwise. * * @return See above. */ public boolean isCancelled() { boolean b = statusLabel.isMarkedAsCancel(); if (b || getFile().isFile()) return b; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { if (i.next().isCancelled()) return true; } return false; } /** * Returns <code>true</code> if the component has imports to cancel, * <code>false</code> otherwise. * * @return See above. */ public boolean hasImportToCancel() { boolean b = statusLabel.isMarkedAsCancel(); if (b) return false; if (getFile().isFile() && !hasImportStarted()) return true; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); FileImportComponent fc; while (i.hasNext()) { fc = i.next(); if (!fc.isCancelled() && !fc.hasImportStarted()) return true; } return false; } /** * Returns <code>true</code> if the file can be re-imported, * <code>false</code> otherwise. * * @return See above. */ public boolean hasFailuresToReimport() { if (getFile().isFile()) return hasUploadFailed() && !reimported; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { if (i.next().hasUploadFailed()) return true; } return false; } /** * Returns <code>true</code> if the file can be re-imported, * <code>false</code> otherwise. * * @return See above. */ public boolean hasFailuresToReupload() { if (getFile().isFile()) return hasUploadFailed() && !reimported; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { if (i.next().hasFailuresToReupload()) return true; } return false; } /** * Returns <code>true</code> if the import has failed, <code>false</code> * otherwise. * * @return See above. */ public boolean hasImportStarted() { if (getFile().isFile()) return resultIndex != ImportStatus.QUEUED; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); int count = 0; while (i.hasNext()) { if (i.next().hasImportStarted()) count++; } return count == components.size(); } /** * Returns <code>true</code> the error can be submitted, <code>false</code> * otherwise. * * @return See above. */ public boolean hasFailuresToSend() { if (getFile().isFile()) return resultIndex == ImportStatus.FAILURE; if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { if (i.next().hasFailuresToSend()) return true; } return false; } /** * Returns <code>true</code> if the folder has components added, * <code>false</code> otherwise. * * @return See above. */ public boolean hasComponents() { return components != null && components.size() > 0; } /** * Lays out the entries. * * @param failure Pass <code>true</code> to display the failed import only, * <code>false</code> to display all the entries. */ public void layoutEntries(boolean failure) { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.setBorder(BorderFactory.createLineBorder(Color.BLACK)); if (!hasComponents()) return; Entry<File, FileImportComponent> e; Iterator<Entry<File, FileImportComponent>> i = components.entrySet().iterator(); int index = 0; FileImportComponent fc; if (failure) { while (i.hasNext()) { e = i.next(); fc = e.getValue(); if (fc.hasImportFailed()) { if (index%2 == 0) fc.setBackground(UIUtilities.BACKGROUND_COLOUR_EVEN); else fc.setBackground(UIUtilities.BACKGROUND_COLOUR_ODD); p.add(fc); index++; } } } else { while (i.hasNext()) { e = i.next(); fc = e.getValue(); if (index%2 == 0) fc.setBackground(UIUtilities.BACKGROUND_COLOUR_EVEN); else fc.setBackground(UIUtilities.BACKGROUND_COLOUR_ODD); p.add(fc); index++; } } pane.removeAll(); pane.add(p); pane.revalidate(); pane.repaint(); } /** * Returns the status of the import process one of the * values defined in @see ImportStatus * * @return See above. */ public ImportStatus getImportStatus() { if (getFile().isFile()) { if (hasImportFailed()) return ImportStatus.FAILURE; return resultIndex; } if (components == null || components.size() == 0) { if (image instanceof Boolean) { if (getFile().isDirectory()) { return ImportStatus.SUCCESS; } else { if (!statusLabel.isMarkedAsCancel() && !statusLabel.isMarkedAsDuplicate()) return ImportStatus.FAILURE; } } return resultIndex; } Iterator<FileImportComponent> i = components.values().iterator(); int n = components.size(); int count = 0; while (i.hasNext()) { if (i.next().hasImportFailed()) count++; } if (count == n) return ImportStatus.FAILURE; if (count > 0) return ImportStatus.PARTIAL; return ImportStatus.SUCCESS; } /** * Returns <code>true</code> if refresh whole tree, <code>false</code> * otherwise. * * @return See above. */ public boolean hasToRefreshTree() { if (getFile().isFile()) { if (hasImportFailed()) return false; switch (type) { case PROJECT_TYPE: case NO_CONTAINER: return true; default: return false; } } if (components == null) return false; if (importable.isFolderAsContainer() && type != PROJECT_TYPE) { Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { if (i.next().toRefresh()) return true; } return false; } return true; } /** * Returns <code>true</code> if some files were imported, * <code>false</code> otherwise. * * @return See above. */ public boolean toRefresh() { /* if (file.isFile()) { if (deleteButton.isVisible()) return false; else if (errorBox.isVisible()) return !(errorBox.isEnabled() && errorBox.isSelected()); return true; } if (components == null) return false; Iterator<FileImportComponent> i = components.values().iterator(); int count = 0; while (i.hasNext()) { if (i.next().hasFailuresToSend()) count++; } return components.size() != count; */ return true; } /** Indicates the import has been cancelled. */ public void cancelLoading() { if (components == null || components.isEmpty()) { cancel(getFile().isFile()); return; } Iterator<FileImportComponent> i = components.values().iterator(); while (i.hasNext()) { i.next().cancelLoading(); } } /** * Sets the type. * * @param type One of the constants defined by this class. */ public void setType(int type) { this.type = type; } /** * Returns the supported type. One of the constants defined by this class. * * @return See above. */ public int getType() { return type; } /** * Returns <code>true</code> if the folder has been converted into a * container, <code>false</code> otherwise. * * @return See above. */ public boolean isFolderAsContainer() { return importable.isFolderAsContainer(); } /** * Returns the object corresponding to the folder. * * @return See above. */ public DataObject getContainerFromFolder() { return containerFromFolder; } /** * Returns <code>true</code> if the file has already been marked for * re-import, <code>false</code> otherwise. * * @return See above. */ public List<FileImportComponent> getFilesToReupload() { List<FileImportComponent> l = null; if (getFile().isFile()) { if (hasFailuresToReupload() && !reimported) { return Arrays.asList(this); } } else { if (components != null) { Iterator<FileImportComponent> i = components.values().iterator(); FileImportComponent fc; l = new ArrayList<FileImportComponent>(); List<FileImportComponent> list; while (i.hasNext()) { fc = i.next(); list = fc.getFilesToReupload(); if (!CollectionUtils.isEmpty(list)) l.addAll(list); } } } return l; } /** * Sets to <code>true</code> to mark the file for reimport. * <code>false</code> otherwise. * * @param reimported Pass <code>true</code> to mark the file for reimport, * <code>false</code> otherwise. */ public void setReimported(boolean reimported) { this.reimported = reimported; repaint(); } /** * Sets the result of the import for the specified file. * * @param result The result. */ public void uploadComplete(Object result) { if (result instanceof CmdCallback) callback = (CmdCallback) result; } /** * Returns the index associated to the main component. * * @return See above. */ public int getIndex() { return index; } /** * Returns the result of the import either a collection of * <code>PixelsData</code> or an exception. * * @return See above. */ public Object getImportResult() { return statusLabel.getImportResult(); } /** * Returns <code>true</code> if it is a HCS file, <code>false</code> * otherwise. * * @return See above. */ public boolean isHCS() { return statusLabel.isHCS(); } /** * Returns the size of the upload. * * @return See above. */ public long getImportSize() { return statusLabel.getFileSize(); } /** * Returns <code>true</code> if the result has already been set, * <code>false</code> otherwise. * * @return See above. */ public boolean hasResult() { return image != null; } /** * Returns the importable object associated to the parent, * <code>null</code> if no parent. * * @return See above. */ public ImportableFile getImportableFile() { return importable; } /** * Indicates the results saving status. * * @param message The message to display * @param busy Pass <code>true</code> when saving, * <code>false</code> otherwise. */ public void onResultsSaving(String message, boolean busy) { statusLabel.updatePostProcessing(message, !busy); busyLabel.setVisible(busy); busyLabel.setBusy(busy); } /** * Overridden to make sure that all the components have the correct * background. * @see JPanel#setBackground(Color) */ public void setBackground(Color color) { if (busyLabel != null) busyLabel.setBackground(color); if (namePane != null) { namePane.setBackground(color); for (int i = 0; i < namePane.getComponentCount(); i++) namePane.getComponent(i).setBackground(color); } super.setBackground(color); } /** * Listens to property fired by the <code>StatusLabel</code>. * @see PropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (StatusLabel.FILES_SET_PROPERTY.equals(name)) { if (isCancelled()) { busyLabel.setBusy(false); busyLabel.setVisible(false); return; } Map<File, StatusLabel> files = (Map<File, StatusLabel>) evt.getNewValue(); int n = files.size(); insertFiles(files); firePropertyChange(IMPORT_FILES_NUMBER_PROPERTY, null,n); } else if (StatusLabel.FILE_IMPORT_STARTED_PROPERTY.equals(name)) { resultIndex = ImportStatus.STARTED; StatusLabel sl = (StatusLabel) evt.getNewValue(); if (sl.equals(statusLabel) && busyLabel != null) { cancelButton.setEnabled(sl.isCancellable()); firePropertyChange(StatusLabel.FILE_IMPORT_STARTED_PROPERTY, null, this); } } else if (StatusLabel.UPLOAD_DONE_PROPERTY.equals(name)) { StatusLabel sl = (StatusLabel) evt.getNewValue(); if (sl.equals(statusLabel) && hasParent()) { if (sl.isMarkedAsCancel()) cancel(true); else { formatResult(); firePropertyChange(StatusLabel.UPLOAD_DONE_PROPERTY, null, this); } } } else if (StatusLabel.CANCELLABLE_IMPORT_PROPERTY.equals(name)) { StatusLabel sl = (StatusLabel) evt.getNewValue(); if (sl.equals(statusLabel)) cancelButton.setVisible(sl.isCancellable()); } else if (StatusLabel.SCANNING_PROPERTY.equals(name)) { StatusLabel sl = (StatusLabel) evt.getNewValue(); if (sl.equals(statusLabel)) { if (busyLabel != null && !isCancelled()) { busyLabel.setBusy(true); busyLabel.setVisible(true); } } } else if (StatusLabel.FILE_RESET_PROPERTY.equals(name)) { importable.setFile((File) evt.getNewValue()); fileNameLabel.setText(getFile().getName()); } else if (ThumbnailLabel.BROWSE_PLATE_PROPERTY.equals(name)) { firePropertyChange(BROWSE_PROPERTY, evt.getOldValue(), evt.getNewValue()); } else if (StatusLabel.CONTAINER_FROM_FOLDER_PROPERTY.equals(name)) { containerFromFolder = (DataObject) evt.getNewValue(); if (containerFromFolder instanceof DatasetData) { containerObject = containerFromFolder; } else if (containerFromFolder instanceof ScreenData) { containerObject = containerFromFolder; } } else if (StatusLabel.DEBUG_TEXT_PROPERTY.equals(name)) { firePropertyChange(name, evt.getOldValue(), evt.getNewValue()); } else if (ThumbnailLabel.VIEW_IMAGE_PROPERTY.equals(name)) { //use the group SecurityContext ctx = new SecurityContext( importable.getGroup().getId()); EventBus bus = ImporterAgent.getRegistry().getEventBus(); Long id = (Long) evt.getNewValue(); bus.post(new ViewImage(ctx, new ViewImageObject(id), null)); } else if (StatusLabel.IMPORT_DONE_PROPERTY.equals(name) || StatusLabel.PROCESSING_ERROR_PROPERTY.equals(name)) { StatusLabel sl = (StatusLabel) evt.getNewValue(); if (sl.equals(statusLabel)) firePropertyChange(StatusLabel.IMPORT_DONE_PROPERTY, null, this); } } /** * Returns the name of the file and group's id and user's id. * @see #toString() */ public String toString() { StringBuffer buf = new StringBuffer(); buf.append(getFile().getAbsolutePath()); if (importable.getGroup() != null) buf.append("_"+importable.getGroup().getId()); if (importable.getUser() != null) buf.append("_"+importable.getUser().getId()); return buf.toString(); } }