/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2014 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.env.data.views.calls;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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 org.apache.commons.collections.CollectionUtils;
import org.openmicroscopy.shoola.env.LookupNames;
import org.openmicroscopy.shoola.env.config.AgentInfo;
import org.openmicroscopy.shoola.env.config.Registry;
import org.openmicroscopy.shoola.env.data.AdminService;
import org.openmicroscopy.shoola.env.data.OmeroDataService;
import org.openmicroscopy.shoola.env.data.OmeroMetadataService;
import org.openmicroscopy.shoola.env.data.model.TimeRefObject;
import omero.gateway.SecurityContext;
import org.openmicroscopy.shoola.env.data.views.BatchCall;
import org.openmicroscopy.shoola.env.data.views.BatchCallTree;
import omero.gateway.model.DataObject;
import omero.gateway.model.DatasetData;
import omero.gateway.model.ExperimenterData;
import omero.gateway.model.FileAnnotationData;
import omero.gateway.model.GroupData;
import omero.gateway.model.ImageData;
import omero.gateway.model.ProjectData;
import omero.gateway.model.ScreenData;
import omero.gateway.model.TagAnnotationData;
/**
* Command to refresh a data trees.
*
* @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 OME2.2
*/
public class DMRefreshLoader
extends BatchCallTree
{
/** The results of the call. */
private Object results;
/** Loads the specified tree. */
private BatchCall loadCall;
/**
* Retrieve the data.
*
* @param rootNodeType The root node.
* @param nodes The nodes to handle.
* @param mapResult Map hosting the results.
* @throws Exception Thrown if an error occurred.
*/
private void retrieveData(Class rootNodeType,
Map<SecurityContext, List> nodes,
Map<SecurityContext, Object> mapResult)
throws Exception
{
OmeroDataService os = context.getDataService();
Iterator<Entry<SecurityContext, List>>
users = nodes.entrySet().iterator();
long userID;
List containers;
Object result;
Set set, children, newChildren, r;
List<Long> ids;
Iterator i, j, c, k;
Long id;
Class klass;
Map topNodes;
DataObject child, parent;
Set s;
Entry<SecurityContext, List> entry;
SecurityContext ctx;
TimeRefObject ref;
Object object;
while (users.hasNext()) {
entry = users.next();
ctx = entry.getKey();
userID = ctx.getExperimenter();
containers = entry.getValue();
if (CollectionUtils.isEmpty(containers)) {
result = os.loadContainerHierarchy(ctx, rootNodeType, null,
false, ctx.getExperimenter());
if (mapResult.containsKey(ctx)) {
s = (Set) mapResult.get(userID);
s.addAll((Set) result);
} else {
mapResult.put(ctx, result);
}
} else {
//First need to extract any TimeRefObject
topNodes = new HashMap();
i = containers.iterator();
ids = new ArrayList<Long>();
while (i.hasNext()) {
object = i.next();
if (object instanceof TimeRefObject) {
ref = (TimeRefObject) object;
if (ref.getFileType() == TimeRefObject.FILE_IMAGE_TYPE)
ref.setResults(
os.getExperimenterImages(ctx, userID, true));
topNodes.put(ref, ref);
} else {
ids.add(Long.valueOf(((DataObject) object).getId()));
}
}
//load the rest.
set = os.loadContainerHierarchy(ctx, rootNodeType, null,
false, userID);
j = set.iterator();
children = null;
klass = null;
while (j.hasNext()) {
newChildren = new HashSet();
parent = (DataObject) j.next();
if (parent instanceof ProjectData) {
children = ((ProjectData) parent).getDatasets();
klass = DatasetData.class;
} else if (parent instanceof ScreenData) {
children = null;//((ScreenData) parent).getPlates();
klass = ScreenData.class;
} else if (parent instanceof DatasetData) {
children = new HashSet(1);
children.add(parent);
klass = DatasetData.class;
}
topNodes.put(parent, newChildren);
if (children != null) {
c = children.iterator();
while (c.hasNext()) {
child = (DataObject) c.next();
id = Long.valueOf(child.getId());
if (ids.contains(id)) {
r = os.loadContainerHierarchy(ctx, klass,
Arrays.asList(id), true, userID);
k = r.iterator();
while (k.hasNext()) {
newChildren.add(k.next());
}
} else newChildren.add(child);
}
}
}
result = topNodes;
if (mapResult.containsKey(ctx)) {
Map map = (Map) mapResult.get(userID);
map.putAll((Map) result);
} else mapResult.put(ctx, result);
}
}
}
/**
* Creates a {@link BatchCall} to retrieve a Container tree, either
* <code>Project</code> or <code>Screen</code>.
*
* @param rootNodeType The type of the root node. Can either:
* {@link ProjectData}.
* @param nodes The nodes to handle.
* @return The {@link BatchCall}.
*/
private BatchCall makeBatchCall(final Class rootNodeType,
final Map<SecurityContext, List> nodes)
{
return new BatchCall("Loading container tree: ") {
public void doCall() throws Exception
{
Map<SecurityContext, Object> r =
new HashMap<SecurityContext, Object>(nodes.size());
results = r;
retrieveData(rootNodeType, nodes, r);
}
};
}
/**
* Creates a {@link BatchCall} to retrieve the images.
*
* @param nodes The map whose keys are the security context and the values
* are the corresponding collections of data objects to reload.
* @return The {@link BatchCall}.
*/
private BatchCall makeImagesBatchCall(final Map<SecurityContext, List> nodes)
{
return new BatchCall("Loading images: ") {
public void doCall() throws Exception
{
OmeroDataService os = context.getDataService();
Iterator i = nodes.entrySet().iterator();
long userID;
List containers;
Iterator j ;
TimeRefObject ref;
Entry entry;
SecurityContext ctx;
while (i.hasNext()) {
entry = (Entry) i.next();
ctx = (SecurityContext) entry.getKey();
userID = ctx.getExperimenter();
containers = (List) entry.getValue();
j = containers.iterator();
while (j.hasNext()) {
ref = (TimeRefObject) j.next();
ref.setResults(os.getImagesPeriod(ctx,
ref.getStartTime(), ref.getEndTime(), userID,
true));
}
}
results = nodes;
}
};
}
/**
* Returns all the groups the user is a member of.
*
* @return See above.
*/
private Collection getAllGroups()
{
return (Collection) context.lookup(LookupNames.USER_GROUP_DETAILS);
}
/**
* Returns the collection of groups the current user is the leader of.
*
* @return See above.
*/
public Set getGroupsLeaderOf()
{
Set values = new HashSet();
Collection groups = getAllGroups();
Iterator i = groups.iterator();
GroupData g;
Set leaders;
ExperimenterData exp = (ExperimenterData) context.lookup(
LookupNames.CURRENT_USER_DETAILS);
long id = exp.getId();
Iterator j;
while (i.hasNext()) {
g = (GroupData) i.next();
leaders = g.getLeaders();
if (leaders != null && leaders.size() > 0) {
j = leaders.iterator();
while (j.hasNext()) {
exp = (ExperimenterData) j.next();
if (exp.getId() == id) {
values.add(g);
break;
}
}
}
}
return values;
}
/**
* Creates a {@link BatchCall} to retrieve the groups.
*
* @param nodes The map whose keys are the security context and the values
* are the corresponding collections of data objects to reload.
* @return The {@link BatchCall}.
*/
private BatchCall makeGroupsBatchCall(final Map<SecurityContext, List> nodes)
{
return new BatchCall("Loading groups: ") {
public void doCall() throws Exception
{
AdminService svc = context.getAdminService();
//Check if the user is an administrator
Boolean admin = (Boolean)
context.lookup(LookupNames.USER_ADMINISTRATOR);
if (admin != null && admin.booleanValue()) {
Iterator<Entry<SecurityContext, List>> i =
nodes.entrySet().iterator();
Entry<SecurityContext, List> e;
SecurityContext ctx;
while (i.hasNext()) {
e = i.next();
ctx = e.getKey();
List<GroupData> groups = svc.loadGroups(ctx, -1);
List<GroupData> r = new ArrayList<GroupData>();
List<Long> toRemove = new ArrayList<Long>();
List<GroupData> l;
List list;
list = e.getValue();
Iterator j = list.iterator();
while (j.hasNext()) {
long groupID = (Long) j.next();
l = svc.loadGroups(ctx, groupID);
toRemove.add(groupID);
if (l.size() == 1) r.add(l.get(0));
}
Iterator<GroupData> k = groups.iterator();
GroupData g;
while (k.hasNext()) {
g = (GroupData) k.next();
if (!toRemove.contains(g.getId()))
r.add(g);
}
results = r;
}
} else { //Not admin groups owner.
Collection allgroups = getAllGroups();
Collection groups = getGroupsLeaderOf();
Iterator i = groups.iterator();
GroupData group;
SecurityContext ctx;
List<GroupData> l = new ArrayList<GroupData>();
while (i.hasNext()) {
group = (GroupData) i.next();
ctx = new SecurityContext(group.getId());
l.addAll(svc.loadGroups(ctx, group.getId()));
allgroups.remove(group);
}
Collection all = new ArrayList();
all.addAll(l);
all.addAll(allgroups);
context.bind(LookupNames.USER_GROUP_DETAILS, all);
List agents = (List) context.lookup(LookupNames.AGENTS);
Iterator kk = agents.iterator();
AgentInfo agentInfo;
Registry reg;
while (kk.hasNext()) {
agentInfo = (AgentInfo) kk.next();
if (agentInfo.isActive()) {
reg = agentInfo.getRegistry();
reg.bind(LookupNames.USER_GROUP_DETAILS, all);
}
}
results = l;
}
}
};
}
/**
* Creates a {@link BatchCall} to retrieve the files.
*
* @param nodes The map whose keys are the security context and the values
* are the corresponding collections of data objects to reload.
* @return The {@link BatchCall}.
*/
private BatchCall makeFilesBatchCall(final Map<SecurityContext, List> nodes)
{
return new BatchCall("Loading files: ") {
public void doCall() throws Exception
{
OmeroMetadataService os = context.getMetadataService();
//Iterator users = nodes.keySet().iterator();
long userID;
List containers;
Iterator j ;
TimeRefObject ref;
Entry entry;
SecurityContext ctx;
Iterator i = nodes.entrySet().iterator();
while (i.hasNext()) {
entry = (Entry) i.next();
ctx = (SecurityContext) entry.getKey();
userID = ctx.getExperimenter();
containers = (List) entry.getValue();
j = containers.iterator();
while (j.hasNext()) {
ref = (TimeRefObject) j.next();
ref.setResults(os.loadFiles(ctx, ref.getFileType(),
userID));
}
}
results = nodes;
}
};
}
/**
* Creates a {@link BatchCall} to retrieve the files.
*
* @param nodes The map whose keys are the security context and the values
* are the corresponding collections of data objects to reload.
* @return The {@link BatchCall}.
*/
private BatchCall makeTagsBatchCall(final Map<SecurityContext, List> nodes)
{
return new BatchCall("Loading files: ") {
public void doCall() throws Exception
{
OmeroMetadataService os = context.getMetadataService();
OmeroDataService ds = context.getDataService();
Map<SecurityContext, Object> r =
new HashMap<SecurityContext, Object>(nodes.size());
long userID;
Entry<SecurityContext, List> entry;
Iterator<Entry<SecurityContext, List>>
j = nodes.entrySet().iterator();
List<?> l;
Collection tags;
Iterator k;
Iterator<TagAnnotationData> i;
TagAnnotationData tag, child;
Map<Long, Collection<?>> values;
SecurityContext ctx;
Map<DataObject, Set<?>> mapForDataObject;
while (j.hasNext()) {
entry = j.next();
ctx = entry.getKey();
userID = ctx.getExperimenter();
l = entry.getValue();
tags = os.loadTags(ctx, -1L, true, userID, ctx.getGroupID());
List<Object> tagResults = new ArrayList<Object>();
mapForDataObject = new HashMap<DataObject, Set<?>>();
if (CollectionUtils.isEmpty(l)) {
r.put(ctx, tags);
} else {
values = new HashMap<Long, Collection<?>>();
k = l.iterator();
Object ob;
TimeRefObject ref;
DataObject ho;
List<TagAnnotationData> refTags =
new ArrayList<TagAnnotationData>();
while (k.hasNext()) {
ob = k.next();
if (ob instanceof TagAnnotationData) {
tag = (TagAnnotationData) ob;
values.put(tag.getId(), os.loadTags(ctx,
tag.getId(), false, userID,
ctx.getGroupID()));
} else if (ob instanceof TimeRefObject) {
ref = (TimeRefObject) ob;
ref.setResults(os.loadFiles(ctx,
ref.getFileType(), userID));
refTags.addAll(ref.getResults());
tagResults.add(ref);
} else if (ob instanceof DataObject) {
//retrieve the data for the data object
ho = (DataObject) ob;
mapForDataObject.put(ho,
ds.loadContainerHierarchy(ctx,
ob.getClass(), Arrays.asList(ho.getId()),
true, userID));
}
}
handleTags(tags, tagResults, values, mapForDataObject);
if (refTags.size() > 0) {
handleTags(refTags, null, values, mapForDataObject);
}
r.put(ctx, tagResults);
}
}
results = r;
}
};
}
/**
* Maps the tags to the correct objects.
*
* @param tags The tags to handle.
* @param tagResults The tags loading for smart folder.
* @param values The value for the loaded objects.
* @param mapForDataObject The loaded values.
*/
private void handleTags(Collection<TagAnnotationData> tags,
List<Object> tagResults, Map<Long, Collection<?>> values,
Map<DataObject, Set<?>> mapForDataObject)
{
Iterator<TagAnnotationData> k = tags.iterator();
TagAnnotationData tag, child;
String ns;
Set<TagAnnotationData> set;
Iterator<TagAnnotationData> i;
while (k.hasNext()) {
tag = (TagAnnotationData) k.next();
if (tagResults != null) tagResults.add(tag);
ns = tag.getNameSpace();
if (TagAnnotationData.INSIGHT_TAGSET_NS.equals(ns)) {
set = tag.getTags();
i = set.iterator();
while (i.hasNext()) {
child = i.next();
if (values.containsKey(child.getId())) {
populateTag(child, values, mapForDataObject);
}
}
} else {
if (values.containsKey(tag.getId())) {
populateTag(tag, values, mapForDataObject);
}
}
}
}
/**
* Populates the specified tag with the passed value.
*
* @param tag The tag to populate
* @param values Track the object.
* @param mapForDataObject The values to set.
*/
private void populateTag(TagAnnotationData tag,
Map<Long, Collection<?>> values,
Map<DataObject, Set<?>> mapForDataObject)
{
if (mapForDataObject.isEmpty()) {
tag.setDataObjects((Set<DataObject>) values.get(tag.getId()));
} else {
Set<DataObject> objects = (Set<DataObject>) values.get(tag.getId());
Set<DataObject> newList = new HashSet<DataObject>(objects.size());
Iterator<DataObject> kk = objects.iterator();
while (kk.hasNext()) {
newList.add(getLoadedObject(mapForDataObject, kk.next()));
}
tag.setDataObjects(newList);
}
}
/**
* Checks the object has been reloaded.
*
* @param map The list of reloaded object.
* @param ho The data object of reference.
*/
private DataObject getLoadedObject(Map<DataObject, Set<?>> map,
DataObject ho)
{
Set<DataObject> sets = map.keySet();
Iterator<DataObject> i = sets.iterator();
DataObject object;
Set<?> s;
while (i.hasNext()) {
object = i.next();
if (object.getClass().equals(ho.getClass()) &&
object.getId() == ho.getId()) {
s = map.get(object);
return (DataObject) s.iterator().next();
} else if (ho instanceof ProjectData) { //need to check the dataset
ProjectData p = (ProjectData) ho;
Set<DatasetData> datasets = p.getDatasets();
Iterator<DatasetData> j = datasets.iterator();
Set<DatasetData> loaded = new HashSet<DatasetData>();
boolean modified = false;
while (j.hasNext()) {
DataObject data = j.next();
if (object.getClass().equals(data.getClass()) &&
object.getId() == data.getId()) {
s = map.get(object);
loaded.add((DatasetData) s.iterator().next());
modified = true;
} else loaded.add((DatasetData) data);
}
if (modified) {
p.setDatasets(loaded);
return p;
}
}
}
return ho;
}
/**
* Adds the {@link #loadCall} to the computation tree.
* @see BatchCallTree#buildTree()
*/
protected void buildTree() { add(loadCall); }
/**
* Returns the root node of the requested tree.
* @see BatchCallTree#getResult()
*/
protected Object getResult() { return results; }
/**
* Creates a new instance.
* If bad arguments are passed, we throw a runtime
* exception so to fail early and in the caller's thread.
*
* @param rootNodeType The type of the root node.
* @param nodes The map whose keys are the security context
* and the values are the corresponding collections of
* data objects to reload.
*/
public DMRefreshLoader(Class rootNodeType,
Map<SecurityContext, List> nodes)
{
if (rootNodeType == null)
throw new IllegalArgumentException("No root node type.");
if (nodes == null || nodes.size() == 0)
throw new IllegalArgumentException("No container with images.");
if (ImageData.class.equals(rootNodeType))
loadCall = makeImagesBatchCall(nodes);
else if (ProjectData.class.equals(rootNodeType) ||
ScreenData.class.equals(rootNodeType))
loadCall = makeBatchCall(rootNodeType, nodes);
else if (FileAnnotationData.class.equals(rootNodeType)) {
loadCall = makeFilesBatchCall(nodes);
} else if (TagAnnotationData.class.equals(rootNodeType)) {
loadCall = makeTagsBatchCall(nodes);
} else if (GroupData.class.equals(rootNodeType)) {
loadCall = makeGroupsBatchCall(nodes);
}
}
}