/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2016 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.dataBrowser.view;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.prefs.Preferences;
import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplay;
import org.openmicroscopy.shoola.agents.util.browser.TreeImageTimeSet;
import omero.gateway.SecurityContext;
import omero.gateway.model.SearchResultCollection;
import org.openmicroscopy.shoola.env.rnd.RndProxyDef;
import omero.gateway.model.DataObject;
import omero.gateway.model.DatasetData;
import omero.gateway.model.ExperimenterData;
import omero.gateway.model.FileData;
import omero.gateway.model.GroupData;
import omero.gateway.model.ImageData;
import omero.gateway.model.PlateAcquisitionData;
import omero.gateway.model.PlateData;
import omero.gateway.model.ProjectData;
import omero.gateway.model.TagAnnotationData;
import omero.gateway.model.WellData;
/**
* Factory to create {@link DataBrowser} components.
* This class keeps track of all {@link DataBrowser} instances that have been
* created and are not yet {@link DataBrowser#DISCARDED discarded}. A new
* component is only created if none of the <i>tracked</i> ones is already
* displaying the given hierarchy. Otherwise, the existing component is
* recycled.
*
* @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>
* @version 3.0
* @since OME3.0
*/
public class DataBrowserFactory
{
/** The sole instance. */
private static final DataBrowserFactory
singleton = new DataBrowserFactory();
/** The name of the thumbnail scale factor property */
private static final String THUMBNAIL_SCALE_FACTOR = "thumbnailScaleFactor";
/** Discards all the tracked {@link DataBrowser}s. */
public static final void discardAll()
{
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.discard();
singleton.discardedBrowsers.add((String) entry.getKey());
}
System.gc();
singleton.browsers.clear();
}
/**
* Creates a browser to display the results.
*
* @param result The value to set.
* @return See above.
*/
public static final DataBrowser getSearchBrowser(
Map<SecurityContext, Collection<DataObject>> result)
{
return singleton.createSearchDataBrowser(result);
}
/**
* Creates a browser to display the results.
*
* @param results The value to set.
* @return See above.
*/
public static final DataBrowser getSearchBrowser(SearchResultCollection results)
{
return singleton.createSearchDataBrowser(results);
}
/**
* Returns the browser used for searching data.
*
* @return See above.
*/
public static final DataBrowser getSearchBrowser()
{
return singleton.searchBrowser;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of images.
*
* @param ctx The security context.
* @param ancestors Map containing the ancestors of the node.
* @param parent The parent's node.
* @param wells The collection to set.
* @param withThumbnails Pass <code>true</code> to load the thumbnails,
* <code>false</code> otherwise.
* @return See above.
*/
public static final DataBrowser getWellsDataBrowser(
SecurityContext ctx, Map<Class, Object> ancestors, Object parent,
Set<WellData> wells, boolean withThumbnails)
{
return singleton.createWellsDataBrowser(ctx, ancestors, parent, wells,
withThumbnails);
}
/**
* Creates a new {@link DataBrowser} for the passed collection of images.
*
* @param ctx The security context.
* @param grandParent The grandparent of the node.
* @param parent The parent's node.
* @param images The collection to set.
* @param node The node to handle.
* @return See above.
*/
public static final DataBrowser getDataBrowser(
SecurityContext ctx, Object grandParent,
Object parent, Collection<ImageData> images,
TreeImageDisplay node)
{
return singleton.createImagesDataBrowser(ctx, grandParent, parent,
images, node);
}
/**
* Creates a new {@link DataBrowser} for the passed collection of images.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param nodes The collection to set.
* @param withImages Pass <code>true</code> to indicate that the images
* are loaded, <code>false</code> otherwise.
* @return See above.
*/
public static final DataBrowser getTagsBrowser(
SecurityContext ctx, TagAnnotationData parent,
Collection<DataObject> nodes, boolean withImages)
{
return singleton.createTagsDataBrowser(ctx, parent, nodes, withImages);
}
/**
* Creates a new {@link DataBrowser} for the passed collection of
* experimenters.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param experimenters The collection to set.
* @return See above.
*/
public static final DataBrowser getGroupsBrowser(
SecurityContext ctx, GroupData parent,
Collection<ExperimenterData> experimenters)
{
return singleton.createGroupsBrowser(ctx, parent, experimenters);
}
/**
* Creates a new {@link DataBrowser} for the passed collection of
* files.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param files The collection to set.
* @return See above.
*/
public static final DataBrowser getFSFolderBrowser(
SecurityContext ctx, FileData parent, Collection<DataObject> files)
{
return singleton.createFSFolderBrowser(ctx, parent, files);
}
/**
* Creates a new {@link DataBrowser} for the passed collection of datasets.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param nodes The collection to set.
* @return See above.
*/
public static final DataBrowser getDataBrowser(SecurityContext ctx,
ProjectData parent, Set<DatasetData> nodes)
{
return singleton.createDatasetsDataBrowser(ctx, parent, nodes);
}
/**
* Creates a new {@link DataBrowser} for the passed node.
*
* @param parent The node.
* @return See above.
*/
public static final DataBrowser getDataBrowser(Object parent)
{
if (parent == null) return null;
return singleton.browsers.get(createKey(parent));
}
/**
* Returns <code>true</code> if a {@link DataBrowser} has been discarded,
* <code>false</code> otherwise.
*
* @param parent The node.
* @return See above.
*/
public static final boolean hasBeenDiscarded(Object parent)
{
if (parent == null) return false;
String key = createKey(parent);
Iterator<String> i = singleton.discardedBrowsers.iterator();
String value;
while (i.hasNext()) {
value = i.next();
if (value.equals(key)) return true;
}
return false;
}
/**
* Refreshes the thumbnails corresponding to the passed image ids.
*
* @param ids The collection of images to handle.
*/
public static final void refreshThumbnails(Collection ids)
{
if (ids != null && ids.size() > 0) {
if (singleton.searchBrowser != null)
singleton.searchBrowser.reloadThumbnails(ids);
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.reloadThumbnails(ids);
}
System.gc();
}
}
/**
* Sets the image to copy the settings from.
*
* @param rndSettingsToCopy The reference image to copy the rendering settings from
* @param rndDefToCopy 'Pending' rendering settings to copy (can be null)
*/
public static final void setRndSettingsToCopy(ImageData rndSettingsToCopy, RndProxyDef rndDefToCopy)
{
singleton.rndSettingsToCopy = rndSettingsToCopy;
singleton.rndDefToCopy = rndDefToCopy;
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.notifyRndSettingsToCopy();
}
if (singleton.searchBrowser != null)
((DataBrowserComponent)
singleton.searchBrowser).notifyRndSettingsToCopy();
}
/**
* Sets the type of object to copy or <code>null</code> if no objects to
* copy.
*
* @param dataToCopy The type of objects to copy.
*/
public static final void setDataToCopy(Class dataToCopy)
{
singleton.dataToCopy = dataToCopy;
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.notifyDataToCopy();
}
if (singleton.searchBrowser != null)
((DataBrowserComponent) singleton.searchBrowser).notifyDataToCopy();
}
/**
* Notifies the model that the user's group has successfully be modified
* if the passed value is <code>true</code>, unsuccessfully
* if <code>false</code>.
*
* @param success Pass <code>true</code> if successful, <code>false</code>
* otherwise.
*/
public static final void onGroupSwitched(boolean success)
{
if (!success) return;
singleton.dataToCopy = null;
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.discard();
}
singleton.browsers.clear();
singleton.discardedBrowsers.clear();
singleton.searchBrowser = null;
}
/**
* Notifies the model that the user has annotated data.
*
* @param containers The objects to handle.
* @param count A positive value if annotations are added, a negative value
* if annotations are removed.
*/
public static void onAnnotated(List<DataObject> containers, int count)
{
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
DataBrowserComponent comp;
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
comp = (DataBrowserComponent) entry.getValue();
comp.onAnnotated(containers, count);
}
}
/**
* Sets the display mode.
*
* @param displayMode The value to set.
*/
public static void setDisplayMode(int displayMode)
{
Iterator<Entry<Object, DataBrowser>>
v = singleton.browsers.entrySet().iterator();
Entry<Object, DataBrowser> entry;
while (v.hasNext()) {
entry = v.next();
entry.getValue().setDisplayMode(displayMode);
}
onGroupSwitched(true);
}
/**
* Returns the thumbnail scale factor
* @return See above.
*/
public static double getThumbnailScaleFactor() {
Preferences p = Preferences.userNodeForPackage(DataBrowserFactory.class);
String value = p.get(THUMBNAIL_SCALE_FACTOR, "0.5");
return Double.parseDouble(value);
}
/**
* Sets the thumbnail scale factor
* @param d The factor
*/
public static void setThumbnailScaleFactor(double d) {
Preferences p = Preferences.userNodeForPackage(DataBrowserFactory.class);
p.put(THUMBNAIL_SCALE_FACTOR, ""+d);
}
/**
* Returns <code>true</code> if there are rendering settings to copy,
* <code>false</code> otherwise.
*
* @return See above.
*/
static boolean hasRndSettingsToCopy()
{
return singleton.rndSettingsToCopy != null || singleton.rndDefToCopy != null;
}
/**
* Returns <code>true</code> if the image to copy the rendering settings
* from is in the specified group, <code>false</code> otherwise.
*
* @param groupID The group to handle.
* @return See above.
*/
static boolean areSettingsCompatible(long groupID)
{
if (!hasRndSettingsToCopy()) return false;
RndProxyDef def = singleton.rndDefToCopy;
ImageData img = singleton.rndSettingsToCopy;
return singleton.rndDefToCopy != null || singleton.rndSettingsToCopy.getGroupId() == groupID;
}
/**
* Returns the type of objects to copy or <code>null</code> if no objects
* selected.
*
* @return See above.
*/
static Class hasDataToCopy() { return singleton.dataToCopy; }
/**
* Creates the key identifying the browser from the specified object.
*
* @param parent The value to handle.
* @return See above.
*/
private static final String createKey(Object parent)
{
String key = parent.toString();
if (parent instanceof DataObject)
key += ((DataObject) parent).getId();
else if (parent instanceof TreeImageDisplay)
key = TreeImageTimeSet.createPath((TreeImageDisplay) parent, key);
return key;
}
/** The collection of discarded browsers. */
private Set<String> discardedBrowsers;
/** Map used to keep track of the browsers. */
private Map<Object, DataBrowser> browsers;
/** The {@link DataBrowser} displaying the result of a search. */
private DataBrowser searchBrowser;
/** The image to copy the rendering settings from. */
private ImageData rndSettingsToCopy;
/** The copied 'pending' rendering settings */
private RndProxyDef rndDefToCopy;
/** The type identifying the object to copy. */
private Class dataToCopy;
/** Creates a new instance. */
private DataBrowserFactory()
{
browsers = new HashMap<Object, DataBrowser>();
discardedBrowsers = new HashSet<String>();
searchBrowser = null;
rndSettingsToCopy = null;
dataToCopy = null;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of wells.
*
* @param ctx The security context.
* @param ancestors Map containing the ancestors of the node.
* @param parent The parent's node.
* @param wells The collection to set.
* @param withThumbnails Pass <code>true</code> to load the thumbnails,
* <code>false</code> otherwise.
* @return See above.
*/
private DataBrowser createWellsDataBrowser(SecurityContext ctx,
Map<Class, Object> ancestors,
Object parent, Set<WellData> wells, boolean withThumbnails)
{
Object p = parent;
Object go = null;
if (parent instanceof PlateAcquisitionData) {
p = ancestors.get(PlateData.class);
if (p == null) return null;
ancestors.remove(PlateData.class);
}
if (ancestors.size() > 0) {
Iterator<Entry<Class, Object>> i = ancestors.entrySet().iterator();
Entry<Class, Object> entry;
while (i.hasNext()) {
entry = i.next();
go = entry.getValue();
break;
}
}
DataBrowserModel model = new WellsModel(ctx, p, wells, withThumbnails);
model.setGrandParent(go);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
if (parent instanceof PlateData) {
PlateData plate = (PlateData) parent;
Set<PlateAcquisitionData> set = plate.getPlateAcquisitions();
if (set != null && set.size() == 1) {
Iterator<PlateAcquisitionData> j = set.iterator();
while (j.hasNext()) {
parent = j.next();
}
}
}
String key = parent.toString();
if (parent instanceof DataObject)
key += ((DataObject) parent).getId();
browsers.put(key, comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of images.
*
* @param ctx The security context.
* @param grandParent The grandParent of the node.
* @param parent The parent's node.
* @param images The collection to set.
* @param experimenter The experimenter associated to the node.
* @return See above.
*/
private DataBrowser createImagesDataBrowser(
SecurityContext ctx, Object grandParent,
Object parent, Collection<ImageData> images,
TreeImageDisplay node)
{
DataBrowserModel model = new ImagesModel(ctx, parent, images);
model.setGrandParent(grandParent);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
String key = parent.toString();
if (parent instanceof DataObject)
key += ((DataObject) parent).getId();
else key = TreeImageTimeSet.createPath(node, key);
browsers.put(key, comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of datasets.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param datasets The collection to set.
* @return See above.
*/
private DataBrowser createDatasetsDataBrowser(SecurityContext ctx,
DataObject parent, Set<DatasetData> datasets)
{
DataBrowserModel model = new DatasetsModel(ctx, parent, datasets);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
StringBuffer buffer = new StringBuffer();
if (parent == null) {
buffer.append(DatasetData.class.toString());
Iterator<DatasetData> i = datasets.iterator();
List<Long> ids = new ArrayList<Long>();
while (i.hasNext()) {
ids.add(((DatasetData) i.next()).getId());
}
sortNodes(ids);
Iterator<Long> j = ids.iterator();
while (j.hasNext()) {
buffer.append(""+(Long) j.next());
}
} else buffer.append(parent.toString()+parent.getId());
browsers.put(buffer.toString(), comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of tags.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param dataObjects The collection to set.
* @param withImages Pass <code>true</code> to indicate that the images
* are loaded, <code>false</code> otherwise.
* @return See above.
*/
private DataBrowser createTagsDataBrowser(SecurityContext ctx,
DataObject parent, Collection<DataObject> dataObjects,
boolean withImages)
{
DataBrowserModel model = new TagsModel(ctx, parent, dataObjects,
withImages);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
String key = parent.toString()+parent.getId();
browsers.put(key, comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of
* experimenters.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param experimenters The collection to set.
* @return See above.
*/
private DataBrowser createGroupsBrowser(SecurityContext ctx,
GroupData parent, Collection<ExperimenterData> experimenters)
{
DataBrowserModel model = new GroupModel(ctx, parent, experimenters);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
String key = parent.toString()+parent.getId();
browsers.put(key, comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed collection of files.
*
* @param ctx The security context.
* @param parent The parent's node.
* @param files The collection to set.
* @return See above.
*/
private DataBrowser createFSFolderBrowser(SecurityContext ctx,
FileData parent, Collection<DataObject> files)
{
DataBrowserModel model = new FSFolderModel(ctx, parent, files);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
String key = parent.toString()+parent.getId();
browsers.put(key, comp);
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed result.
*
* @param result The result of the search.
* @return See above.
*/
private DataBrowser createSearchDataBrowser(
Map<SecurityContext, Collection<DataObject>> result)
{
DataBrowserModel model = new SearchModel(result);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
searchBrowser = comp;
return comp;
}
/**
* Creates a new {@link DataBrowser} for the passed result.
*
* @param result The result of the search.
* @return See above.
*/
private DataBrowser createSearchDataBrowser(SearchResultCollection result)
{
DataBrowserModel model = new AdvancedResultSearchModel(result);
DataBrowserComponent comp = new DataBrowserComponent(model);
model.initialize(comp);
comp.initialize();
searchBrowser = comp;
return comp;
}
/**
* Sorts the passed collection of <code>DataObject</code>s by id.
*
* @param nodes Collection of <code>DataObject</code>s to sort.
*/
private void sortNodes(List nodes)
{
if (nodes == null || nodes.size() == 0) return;
Comparator c = new Comparator() {
public int compare(Object o1, Object o2)
{
long i1 = ((Long) o1), i2 = ((Long) o2);
int v = 0;
if (i1 < i2) v = -1;
else if (i1 > i2) v = 1;
return -v;
}
};
Collections.sort(nodes, c);
}
}