/*
* $Id$
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.api;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ome.annotations.NotNull;
import ome.annotations.Validate;
import ome.model.ILink;
import ome.model.IObject;
import ome.model.containers.Dataset;
import ome.model.containers.Project;
import ome.model.core.Image;
import ome.model.screen.Plate;
import ome.model.screen.Screen;
import ome.parameters.Parameters;
/**
* Provides methods for dealing with the core "Pojos" of OME. Included are:
* Projects, Datasets, Images.
*
* <h3>Read API</h3>
* <p>
* The names of the methods correlate to how the function operates:
* <ul>
* <li><b>load</b>: start at container objects and work down toward the
* leaves, returning hierarchy (Proejct->Dataset->Image</li>
* <li><b>find</b>: start at leave objects and work up to containers,
* returning hierarchy</li>
* <li><b>get</b>: retrieves only leaves in the hierarchy (currently only
* Images)</li>
* </ul>
* </p>
* <h4>Options Mechanism</h4>
* <p>
* The options are used to add some constraints to the generic method e.g. load
* hierarchy trees images <i>for a given user</i>. This mechanism should give
* us enough flexibility to extend the API if necessary, e.g. in some cases we
* might want to retrieve the images with or without annotations
* </p>
* <p>
* Most methods take such an <code>options</code> map which is built on the
* client-side using the {@link Parameters} class. The currently supported
* options are:
* <ul>
* <li><b>annotator</b>(Integer): If key exists but value null, annotations
* are retrieved for all objects in the hierarchy where they exist; if a valid
* experimenterID, annotations are only retrieved for that user. May not be used
* be all methods. <b>Default: all annotations</b></li>
* <li><b>leaves</b>(Boolean): if FALSE omits images from the returned
* hierarchy. May not be used by all methods. <b>Default: true</b></li>
* <li><b>experimenter</b>(Integer): inables filtering on a per-experimenter
* basis. This option has a method-specific (and possibly context-specific)
* meaning. Please see the individual methods.</li>
* <li><b>group</b>(Integer): enables filtering on a per-group basis. The
* <b>experimenter</b> value is ignored if present and instead a similar
* filtering is done using all <b>experimenter</b>s in the given group.
*
* </p>
* <h3>Write API</h3>
* <p>
* As outlined in TODO, the semantics of the Omero write API are based on three
* rules:
* <ol>
* <li>IObject-valued fields for which <code>isLoaded()</code> returns false
* are assumed filtered</li>
* <li>Collection-valued fields that are null are assumed filtered</li>
* <li>Collection-valued fields for which
* <code>getDetails().isFiltered(String collectionName)</code> returns true
* are assumed filtered. TODO: should we accept isFiltered for all fields?
* </ol>
* In each of these cases, the server will reload that given field <b>before</b>
* attempting to save the graph.
* </p>
* <p>
* For all write calls, the options map (see below) must contain the userId and
* the userGroupId for the newly created objects. TODO umask.
* </p>
*
* @author Jean-Marie Burel <a
* href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author <br>
* Josh Moore <a
* href="mailto:josh.moore@gmx.de"> josh.moore@gmx.de</a>
* @version 1.0
* @since OME1.0
* TODO possibly move optionBuilder to the common code. ome.common.utils
* TODO possibly move map description to marker interface with a see also
* link
* TODO add throws statements where necessary (IllegalArgument, ...)
*
*/
public interface IContainer extends ServiceInterface {
// ~ READ API
// =========================================================================
/**
* Retrieves hierarchy trees rooted by a given node (unless orphan is
* specified -- See below)
* <p>
* This method also retrieves the Experimenters linked to the objects in the
* tree. Similarly, all Images will be linked to their Pixel objects if
* included.
* </p>
* <p>
* Note that objects are never duplicated. For example, if an Experimenter
* owns all the objects in the retrieved tree, then those objects will be
* linked to the <i>same</i> instance of
* {@link ome.model.meta.Experimenter}. Or if an
* Image is contained in more than one Dataset in the retrieved tree, then
* all enclosing {@link ome.model.containers.Dataset} objects will point
* to the <i>same</i> {@link ome.model.core.Image} object. And so on.
* </p>
*
* @param rootNodeType The type of the root node. Can be
* {@link Project},
* {@link Dataset},
* {@link Screen} or
* {@link Plate}.
* Cannot be null.
* @param rootNodeIds
* The ids of the root nodes. Can be null if an Experimenter is
* specified in <code>options</code>, otherwise an Exception
* is thrown to prevent all images in the entire database from
* being downloaded.
* @param options
* Parameters as above. <code>annotator</code>, <code>leaves</code>,
* <code>orphan</code>, <code>acquisition data</code> used.
* <code>acquisition data</code> is only relevant for images
* and taken into account if the images are loaded.
* If <code>rootNodeIds==null</code>,
* <code>experimenter|group</code> must be set and filtering
* will be applied at the <i>Class</i>-level; e.g. to retrieve a
* user's Projects, or user's Datasets. If
* <code>rootNodeIds!=null</code>, the result will be filtered
* by the <code>experimenter|group</code> at the
* <code>Image</code> and intermediate levels <i>if available</i>.
* Due to the amount of data potentially linked a Screen/Plate,
* the <code>leaves</code> option is not taken into account.
* when the root node is a {@link Screen}.
* <code>orphan</code> implies that objects which are not contained
* in an object of rootNodeType should also be returned.
* TODO should it be applied at all levels?
* @return a set of hierarchy trees. The requested node as root and all of
* its descendants. The type of the returned value will be
* <code>rootNodeType</code>, unless <code>orphan</code> is specified
* in which case objects of type <code>rootNodeType</code> and below
* may be returned.
*/
public <T extends IObject> Set<IObject> loadContainerHierarchy(@NotNull
Class<T> rootNodeType, @Validate(Long.class)
Set<Long> rootNodeIds, Parameters options);
/**
* Retrieves hierarchy trees in various hierarchies that contain the
* specified Images.
* <p>
* This method will look for all the containers containing the specified
* Images and then for all containers containing those containers and on up
* the container hierarchy.
* </p>
* <p>
* This method returns a <code>Set</code> with all root nodes that were
* found. Every root node is linked to the found objects and so on until the
* leaf nodes, which are {@link Image} objects. Note that the type of any
* root node in the returned set can be the given rootNodeType, any of its
* containees or an {@link Image image}.
* </p>
* <p>
* For example, say that you pass in the ids of six Images: <code>i1, i2,
* i3, i4, i5, i6</code>.
* If the P/D/I hierarchy in the DB looks like this:
* </p>
*
* <pre>
* __p1__
* / \
* _d1_ _d2_ d3
* / \ / \ |
* i1 i2 i3 i4 i5 i6
* </pre>
*
* <p>
* Then the returned set will contain <code>p1, d3, i5, i6</code>. All
* objects will be properly linked up.
* </p>
* <p>
* Finally, this method will <i>only</i> retrieve the nodes that are
* connected in a tree to the specified leaf image nodes. Back to the
* previous example, if <code>d1</code> contained image
* <code>img500</code>, then the returned object would <i>not</i>
* contain <code>img500</code>. In a similar way, if <code>p1</code>
* contained <code>ds300</code> and this dataset weren't linked to any of
* the <code>i1, i2, i3, i4, i5, i6
* </code> images, then <code>ds300</code>
* would <i>not</i> be part of the returned tree rooted by <code>p1</code>.
* </p>
*
* @param rootNodeType
* top-most type which will be searched for Can be
* {@link Project}. Not null.
* @param imagesIds
* Contains the ids of the Images that sit at the bottom of the
* trees. Not null.
* @param options
* Parameters as above. <code>annotator</code> used.
* <code>experimenter|group</code> may be applied at the
* top-level only or at each level in the hierarchy, but will not
* apply to the leaf (Image) level.
* @return A <code>Set</code> with all root nodes that were found.
* TODO decide on use of experimenter option
*/
public <T extends IObject> Set<IObject> findContainerHierarchies(@NotNull
Class<T> rootNodeType, @NotNull
@Validate(Long.class)
Set<Long> imagesIds, Parameters options);
/**
* Retrieve a user's (or all users') images within any given container. For
* example, all images in project, applying temporal filtering or
* pagination.
*
* @param rootNodeType
* A Class which will have its hierarchy searched for Images. Not
* null. TODO types?
* @param rootNodeIds
* A set of ids of type <code>rootNodeType</code> Not null.
* @param options
* Parameters as above. No notion of <code>leaves</code>.
* <code>experimenter|group</code> apply at the Image level.
* OPTIONS: - startTime and/or endTime should be
* Timestamp.valueOf("YYYY-MM-DD hh:mm:ss.ms");
* <p>
* <code>limit</code> and <code>offset</code> are applied at
* the Image-level. That is, calling with Dataset.class,
* limit == 10 and offset == 0 will
* first perform one query to get an effective set of
* rootNodeIds, then getImages will be called with an effective
* rootNodeType of Image.class and the new ids.
* </p>.
* <code>acquisition data</code> is only relevant for images.
* @return A set of images.
* @see Parameters#paginate(Integer, Integer)
* @see Parameters#startTime(java.sql.Timestamp)
* @see Parameters#endTime(java.sql.Timestamp)
*/
public <T extends IObject> Set<Image> getImages(@NotNull
Class<T> rootNodeType, @NotNull
@Validate(Long.class)
Set<Long> rootNodeIds, Parameters options);
/**
* Retrieve images by options.
*
* @param options
* Parameters as above. No notion of <code>leaves</code>.
* <code>experimenter|group</code> apply at the Image level and
* <b>must be present</b>. OPTIONS: - startTime and/or endTime
* should be Timestamp.valueOf("YYYY-MM-DD hh:mm:ss.ms").
* <code>acquisition data</code> is only relevant for images.
* @return A set of images.
*/
public Set<Image> getImagesByOptions(Parameters options);
/**
* Retrieve a user's images.
*
* @param options
* Parameters as above. No notion of <code>leaves</code>.
* <code>experimenter|group</code> apply at the Image level and
* <b>must be present</b>.
* @return A set of images.
*/
public Set<Image> getUserImages(Parameters options);
/**
* Counts the number of members in a collection for a given object. For
* example, if you wanted to retrieve the number of Images contained in a
* Dataset you would pass TODO.
*
* @param type
* The fully-qualified classname of the object to be tested
* @param property
* Name of the property on that class, omitting getters and
* setters.
* @param ids
* Set of Longs, the ids of the objects to test
* @param options
* Parameters. Unused.
* @return A map from id integer to count integer
*/
public Map getCollectionCount(@NotNull String type,
@NotNull String property,
@NotNull @Validate(Long.class) Set<Long> ids, Parameters options);
/**
* Given a list of IDs of certain entity types, calculates which filesets are split such that
* a non-empty proper subset of their images are referenced, directly or indirectly, as being
* included. The return value lists both the fileset IDs and the image IDs in ascending order,
* the image ID lists separated by if they were included.
* Warning: following discussion in trac ticket 11019 the return type may be changed.
* @param included the entities included
* @param options parameters, presently ignored
* @return the partially included filesets
*/
public Map<Long, Map<Boolean, List<Long>>> getImagesBySplitFilesets(Map<Class<? extends IObject>, List<Long>> included,
Parameters options);
/**
* Retrieves a collection with all members initialized ("loaded"). This is
* useful when a collection has been nulled in a previous query.
*
* @param dataObject
* Can be "unloaded".
* @param collectionName
* <code>public final static String</code> from the
* IObject.class
* @param options
* Parameters. Unused.
* @return An initialized collection.
*/
public Collection retrieveCollection(@NotNull IObject dataObject,
@NotNull String collectionName, Parameters options);
// ~ WRITE API
// =========================================================================
/**
* Creates the specified data object.
* <p>
* A placeholder parent object is created if the data object is to be
* put in a collection.
* </p>
* <p>
* For example, if the object is a <code>Dataset</code>, we first create
* a <code>Project</code> as parent then we set the Dataset parent as
* follows: <code>
* //pseudo-code TODO
* Project p = new Project(id,false);
* dataset.addProject(p);
* </code>
* then for each parent relationship a DataObject {@link ILink} is
* created.
*
* @param object
* IObject. Supported: Project, Dataset,
* Annotation, Group, Experimenter. Not null.
* @param options Parameters as above.
* @return the created object
*/
public <T extends IObject> T createDataObject(@NotNull
T object, Parameters options);
/**
* convenience method to save network calls. Loops over the array of
* IObjects calling createDataObject.
*
* @param dataObjects
* Array of Omero <code>IObjects</code>
* @param options
* Parameters as above.
*
* @see #createDataObject(IObject, Parameters)
*/
public IObject[] createDataObjects(@NotNull IObject[] dataObjects,
Parameters options);
/**
* Removes links between OmeroDataObjects e.g Project-Dataset, Dataset-Image
* Note that the objects themselves aren't deleted, only the Link objects.
*
* @param dataObjectLinks
* Not null.
* @param options
* Parameters as above.
*/
public void unlink(@NotNull ILink[] dataObjectLinks, Parameters options);
/**
* Convenience method for creating links. Functionality also availeble
* from {@link #createDataObject(IObject, Parameters)}
*
* @param dataObjectLinks
* Array of links to be created.
* @param options
* Parameters as above.
* @return the created links
*/
public ILink[] link(@NotNull ILink[] dataObjectLinks, Parameters options);
/**
* Updates a data object.
* <p>
* To link or unlink objects to the specified object, we should call the
* methods link or unlink. TODO Or do we use for example
* dataset.setProjects(set of projects) to add. Tink has to be set as
* follows dataset->project and project->dataset.
*
* Alternatively, you can make sure that the collection is <b>exactly</b>
* how it should be in the database. If you can't guarantee this, it's best
* to send all your collections back as <code>null</code>
*
* @param dataObject
* Pojos-based IObject. Supported: supported: Project, Dataset,
* Annotation, Group, Experimenter.
* @param options
* Parameters as above.
* @return created data object
*/
public <T extends IObject> T updateDataObject(@NotNull
T dataObject, Parameters options);
/**
* convenience method to save network calls. Loops over the array of
* IObjects calling updateDataObject.
*
* @param dataObjects
* @param options
* Parameters as above.
* @return created data objects.
* @see #updateDataObject(IObject, Parameters)
*/
public IObject[] updateDataObjects(@NotNull IObject[] dataObjects,
Parameters options);
}