gt: 		i = r.entrySet().iterator();
context: /*
   *------------------------------------------------------------------------------
   *  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.metadata.editor;
  
  import java.awt.Color;
  import java.awt.image.BufferedImage;
  import java.io.File;
  import java.sql.Timestamp;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.Map.Entry;
  import javax.swing.Icon;
  import javax.swing.JFrame;
  
  import org.apache.commons.collections.CollectionUtils;
  import org.openmicroscopy.shoola.util.CommonsLangUtils;
  
  import omero.model.OriginalFile;
  import omero.model.PlaneInfo;
  import omero.model.PlateAcquisition;
  import org.openmicroscopy.shoola.agents.metadata.AcquisitionDataLoader;
  import org.openmicroscopy.shoola.agents.metadata.AnalysisResultsFileLoader;
  import org.openmicroscopy.shoola.agents.metadata.FileAnnotationChecker;
  import org.openmicroscopy.shoola.agents.metadata.AttachmentsLoader;
  import org.openmicroscopy.shoola.agents.metadata.ChannelDataLoader;
  import org.openmicroscopy.shoola.agents.metadata.ChannelDataSaver;
  import org.openmicroscopy.shoola.agents.metadata.DiskSpaceLoader;
  import org.openmicroscopy.shoola.agents.metadata.EditorLoader;
  import org.openmicroscopy.shoola.agents.metadata.EnumerationLoader;
  import org.openmicroscopy.shoola.agents.metadata.FileLoader;
  import org.openmicroscopy.shoola.agents.metadata.FilesetLoader;
  import org.openmicroscopy.shoola.agents.metadata.IconManager;
  import org.openmicroscopy.shoola.agents.metadata.ImageSizeLoader;
  import org.openmicroscopy.shoola.agents.metadata.InstrumentDataLoader;
  import org.openmicroscopy.shoola.agents.metadata.LDAPLoader;
  import org.openmicroscopy.shoola.agents.metadata.MetadataViewerAgent;
  import org.openmicroscopy.shoola.agents.metadata.OriginalMetadataLoader;
  import org.openmicroscopy.shoola.agents.metadata.PasswordEditor;
  import org.openmicroscopy.shoola.agents.metadata.PlaneInfoLoader;
  import org.openmicroscopy.shoola.agents.metadata.ROILoader;
  import org.openmicroscopy.shoola.agents.metadata.RenderingControlLoader;
  import org.openmicroscopy.shoola.agents.metadata.ScriptLoader;
  import org.openmicroscopy.shoola.agents.metadata.ScriptsLoader;
  import org.openmicroscopy.shoola.agents.metadata.TagsLoader;
  import org.openmicroscopy.shoola.agents.metadata.UserPhotoLoader;
  import org.openmicroscopy.shoola.agents.metadata.UserPhotoUploader;
  import org.openmicroscopy.shoola.agents.metadata.browser.Browser;
  import org.openmicroscopy.shoola.agents.metadata.rnd.Renderer;
  import org.openmicroscopy.shoola.agents.metadata.rnd.RendererFactory;
  import org.openmicroscopy.shoola.agents.metadata.util.AnalysisResultsItem;
  import org.openmicroscopy.shoola.agents.metadata.util.DataToSave;
  import org.openmicroscopy.shoola.agents.metadata.view.MetadataViewer;
  import org.openmicroscopy.shoola.agents.util.EditorUtil;
  import org.openmicroscopy.shoola.agents.util.ViewerSorter;
  import org.openmicroscopy.shoola.agents.util.ui.PermissionMenu;
  import org.openmicroscopy.shoola.env.Environment;
  import org.openmicroscopy.shoola.env.LookupNames;
  import org.openmicroscopy.shoola.env.data.AdminService;
  import org.openmicroscopy.shoola.env.data.OmeroImageService;
  import org.openmicroscopy.shoola.env.data.OmeroMetadataService;
  import org.openmicroscopy.shoola.env.data.model.AdminObject;
  import org.openmicroscopy.shoola.env.data.model.AnnotationLinkData;
  import org.openmicroscopy.shoola.env.data.model.DeletableObject;
  import org.openmicroscopy.shoola.env.data.model.DeleteActivityParam;
  import org.openmicroscopy.shoola.env.data.model.DownloadActivityParam;
  import org.openmicroscopy.shoola.env.data.model.DownloadArchivedActivityParam;
  import org.openmicroscopy.shoola.env.data.model.EnumerationObject;
  import org.openmicroscopy.shoola.env.data.model.SaveAsParam;
  import org.openmicroscopy.shoola.env.data.model.ScriptObject;
  import omero.gateway.SecurityContext;
  import org.openmicroscopy.shoola.env.data.util.StructuredDataResults;
  import omero.log.LogMessage;
  import org.openmicroscopy.shoola.env.rnd.RenderingControl;
  import org.openmicroscopy.shoola.env.ui.UserNotifier;
  import org.openmicroscopy.shoola.util.file.modulo.ModuloInfo;
  import org.openmicroscopy.shoola.util.file.modulo.ModuloParser;
  import org.openmicroscopy.shoola.util.ui.component.ObservableComponent;
  
  import omero.gateway.model.AnnotationData;
  import omero.gateway.model.BooleanAnnotationData;
  import omero.gateway.model.ChannelAcquisitionData;
  import omero.gateway.model.ChannelData;
  import omero.gateway.model.DataObject;
  import omero.gateway.model.DatasetData;
  import omero.gateway.model.DoubleAnnotationData;
  import omero.gateway.model.ExperimenterData;
  import omero.gateway.model.FileAnnotationData;
  import omero.gateway.model.FileData;
  import omero.gateway.model.FilesetData;
  import omero.gateway.model.GroupData;
  import omero.gateway.model.ImageAcquisitionData;
  import omero.gateway.model.ImageData;
  import omero.gateway.model.InstrumentData;
  import omero.gateway.model.LongAnnotationData;
  import omero.gateway.model.MapAnnotationData;
  import omero.gateway.model.PermissionData;
  import omero.gateway.model.PixelsData;
  import omero.gateway.model.PlateAcquisitionData;
  import omero.gateway.model.PlateData;
  import omero.gateway.model.ProjectData;
  import omero.gateway.model.RatingAnnotationData;
  import omero.gateway.model.ScreenData;
  import omero.gateway.model.TagAnnotationData;
  import omero.gateway.model.TermAnnotationData;
  import omero.gateway.model.TextualAnnotationData;
  import omero.gateway.model.WellData;
  import omero.gateway.model.WellSampleData;
  import omero.gateway.model.XMLAnnotationData;
  
  /** 
   * The Model component in the <code>EditorViewer</code> MVC triad.
   * This class knows how to store and manipulate the results. 
   * It provides a suitable data loader.
   * The {@link EditorComponent} intercepts the results of data loadings, 
   * feeds them back to this class and fires state transitions as appropriate.
   *
   *
   * @author  Jean-Marie Burel &nbsp;&nbsp;&nbsp;&nbsp;
   * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
   * @author Donald MacDonald &nbsp;&nbsp;&nbsp;&nbsp;
   * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
   * @version 3.0
   * @since OME3.0
   */
  class EditorModel 
  {
  	
      /** Default maximum export size, 12kx12kx image */
      static long DEFAULT_MAX_EXPORT_SIZE = 144000000;
      
  	/** Identifies <code>all</code> the objects.*/
  	static final int ALL = PermissionMenu.ALL;
  	
  	/** Identifies the objects added by current user.*/
  	static final int ME = PermissionMenu.ME;
  	
  	/** Identifies the objects added by others.*/
  	static final int OTHER = PermissionMenu.OTHER;
  	
  	/** The index of the default channel. */
  	static final int	DEFAULT_CHANNEL = 0;
  
  	/** Enum to distinguish between different kind of 
  	 * {@link MapAnnotationData}, see {@link #getMapAnnotations(MapAnnotationType)}
  	 */
  	public static enum MapAnnotationType {
  		USER, OTHER_USERS, OTHER
  	}
  	
  	/** The file namespaces to exclude.*/
  	private final static List<String> EXCLUDED_FILE_NS;
  	
  	static {
  		EXCLUDED_FILE_NS = new ArrayList<String>();
  		EXCLUDED_FILE_NS.add(FileAnnotationData.COMPANION_FILE_NS);
  	}
  	
  	/** The parent of this editor. */
  	private  MetadataViewer			parent;
  	
  	/** Reference to the component that embeds this model. */
  	private Editor					component;
  	
  	/** The object this editor is for. */
  	private Object					refObject;
  
  	/** The parent of the object this editor is for. */
  	private Object					parentRefObject;
  	
  	/** The parent of the object this editor is for. */
  	private Object					gpRefObject;
  	
      /** 
       * Map containing the annotations made by users.
       * The keys are the user, the value the collection of
       * textual annotations.
       */ 
      private Map<Long, List>         textualAnnotationsByUsers;
      
      /** The annotations ordered by date. */
      private List					textualAnnotationsByDate;
      
      /** Collection of loaders. */
      private List<EditorLoader>		loaders;
  
      /** Collection of existing tags if any. */
      private Collection				existingTags;
      
      /** Collection of existing attachments if any. */
      private Collection				existingAttachments;
      
      /** The list of emission wavelengths for a given set of pixels. */
      private Map						emissionsWavelengths;
      
      /** Used to sort the various collections. */
      private ViewerSorter			sorter;
  
  	/** Reference to the browser. */
  	private Browser					browser;
  	
  	/** The image acquisition data. */
  	private ImageAcquisitionData	imageAcquisitionData;
  	
  	/** The instrument data. */
  	private InstrumentData			instrumentData;
  	
  	/** The enumerations related to channel metadata. */
  	private Map<String, List<EnumerationObject>>	channelEnumerations;
  	
  	/** The enumerations related to image metadata. */
  	private Map<String, List<EnumerationObject>>	imageEnumerations;
  	
  	/** The map hosting the channels acquisition data. */
  	private Map<Integer, ChannelAcquisitionData> 	channelAcquisitionDataMap;
  	
  	/** The map hosting the channels plane info. */
  	private Map<Integer, Collection> 	channelPlaneInfoMap;
  	
  	/** Reference to the renderer. */
  	private Renderer 					renderer;
  	
  	/** Collection of annotations to unlink and delete. */
  	private List<AnnotationData>		toDelete;
  	
  	/** Collection of uploaded scripts. */
  	private Map<Long, ScriptObject>		scripts;
  	
  	/** Scripts with a UI. */
  	private List<ScriptObject> scriptsWithUI;
  	
  	/** The file annotation with the original metadata. */
  	private FileAnnotationData originalMetadata;
  	
  	/** The collection of analysis results. */
  	private Map<AnalysisResultsItem, EditorLoader> resultsLoader;
  	
      /** The photo of the current user.*/
      private Map<Long, BufferedImage>	usersPhoto;
      
      /** Flag indicating if the image is a big image or not.*/
      private boolean largeImage;
      
      /** The file set associated to the image if an image is selected.*/
      private Collection<FilesetData> set;
      
      /** Checks if the image is a large image or not. */
      private void fireLargeImageLoading()
      {
      	ImageData img = null;
  		if (refObject instanceof ImageData) {
      		img = (ImageData) refObject;
      	} else if (refObject instanceof WellSampleData) {
      		img = ((WellSampleData) refObject).getImage();
      	}
  		PixelsData data = img.getDefaultPixels();
  		if (data == null) {
  			largeImage = false;
  			return;
  		}
  		ImageSizeLoader loader = new  ImageSizeLoader(component, 
  				parent.getSecurityContext(), data.getId()); 
  		loader.load();
      }
      
  	/**
  	 * Downloads the files.
  	 * 
  	 * @param folder The folder to save the file into.
  	 * @param override Flag indicating to override the existing file if it
       * exists, <code>false</code> otherwise.
  	 */
  	private void downloadFiles(File folder, boolean override)
  	{
  		UserNotifier un = MetadataViewerAgent.getRegistry().getUserNotifier();
  		FileAnnotationData fa = (FileAnnotationData) getRefObject();
  		OriginalFile f = (OriginalFile) fa.getContent();
  		IconManager icons = IconManager.getInstance();
  		
  		DownloadActivityParam activity = new DownloadActivityParam(f,
  				folder, icons.getIcon(IconManager.DOWNLOAD_22));
  		un.notifyActivity(getSecurityContext(), activity);
  		
  		Collection l = parent.getRelatedNodes();
  		if (l == null) return;
  		Iterator i = l.iterator();
  		Object o;
  		while (i.hasNext()) {
  			o = i.next();
  			if (o instanceof FileAnnotationData) {
  				fa = (FileAnnotationData) o;
  				f = (OriginalFile) fa.getContent();
  				if (f.isLoaded()) {
  					activity = new DownloadActivityParam(f,
  							folder, icons.getIcon(IconManager.DOWNLOAD_22));
  				} else {
  					long id = fa.getId();
  					Environment env = (Environment) 
  					MetadataViewerAgent.getRegistry().lookup(LookupNames.ENV);
  					String path = env.getOmeroFilesHome();
  					path += File.separator+fa.getFileName();
  					activity = new DownloadActivityParam(id, 
  							DownloadActivityParam.FILE_ANNOTATION, 
  							new File(path), 
  							icons.getIcon(IconManager.DOWNLOAD_22));
  				}
  				activity.setFileName(fa.getFileName());
  				un.notifyActivity(getSecurityContext(), activity);
  			}
  		}
  	}
  	
  	/** 
  	 * Downloads the archived images. 
  	 * 
  	 * @param file The file where to download the content.
  	 * @param override Flag indicating to override the existing file if it
       * exists, <code>false</code> otherwise.
  	 */
  	private void downloadImages(File file, boolean override)
  	{
  	    List<DataObject> images = new ArrayList<DataObject>();
  	    List<DataObject> l = getSelectedObjects();
  	    if (!CollectionUtils.isEmpty(l)) {
  	        Iterator<DataObject> i = l.iterator();
  	        DataObject o;
  	        List<Long> filesetIds = new ArrayList<Long>();
  	        long id;
  	        ImageData image;
  	        while (i.hasNext()) {
  	            o = i.next();
  	            if (isArchived(o)) {
  	                image = (ImageData) o;
  	                id = image.getFilesetId();
  	                if (id < 0) images.add(image);
  	                else if (!filesetIds.contains(id)) {
  	                    images.add(image);
  	                    filesetIds.add(id);
  	                }
  	            }
  	        }
  	    }
  	    if (!CollectionUtils.isEmpty(images)) {
  	        DownloadArchivedActivityParam p;
  	        UserNotifier un =
  	                MetadataViewerAgent.getRegistry().getUserNotifier();
  	        IconManager icons = IconManager.getInstance();
  	        Icon icon = icons.getIcon(IconManager.DOWNLOAD_22);
  	        SecurityContext ctx = getSecurityContext();
              p = new DownloadArchivedActivityParam(file, images, icon);
              p.setOverride(override);
              un.notifyActivity(ctx, p);
  	    }
  	}
  
      /** 
       * Sorts the passed collection of annotations by date starting with the
       * most recent.
       * 
       * @param annotations   Collection of {@link AnnotationData} linked to 
       *                      the currently edited <code>DataObject</code>.
       */
      private void sortAnnotationByDate(List annotations)
      {
          if (annotations == null || annotations.size() == 0) return;
          Comparator c = new Comparator() {
              public int compare(Object o1, Object o2)
              {
                  Timestamp t1 = ((AnnotationData) o1).getLastModified(),
                            t2 = ((AnnotationData) o2).getLastModified();
                  long n1 = t1.getTime();
                  long n2 = t2.getTime();
                  int v = 0;
                  if (n1 < n2) v = -1;
                  else if (n1 > n2) v = 1;
                  return -v;
              }
          };
          Collections.sort(annotations, c);
      }
      
      /** 
  	 * Sorts the passed nodes by row.
  	 * 
  	 * @param nodes The nodes to sort.
  	 * @return See above.
  	 */
  	private List sortPlane(Collection nodes)
  	{
  		List<Object> l = new ArrayList<Object>();
  		if (nodes == null) return l;
  		Iterator i = nodes.iterator();
  		while (i.hasNext()) {
  			l.add(i.next());
  		}
  		Comparator c = new Comparator() {
              public int compare(Object o1, Object o2)
              {
                  PlaneInfo i1 = (PlaneInfo) o1, i2 = (PlaneInfo) o2;
                  int t1 = i1.getTheT().getValue();
                  int t2 = i2.getTheT().getValue();
                  int v = 0;
                  if (t1 < t2) v = -1;
                  else if (t1 > t2) v = 1;
                  return v;
              }
          };
          Collections.sort(l, c);
  		return l;
  	}
  	
      /** 
       * Sorts the passed collection of enumerations.
       * 
       * @param enumerations   Collection of {@link EnumerationObject}.
       */
      private void sortEnumerations(List<EnumerationObject> enumerations)
      {
          if (enumerations == null || enumerations.size() == 0) return;
          Comparator c = new Comparator() {
              public int compare(Object o1, Object o2)
              {
                  String s1 = ((EnumerationObject) o1).getValue(),
                          s2 = ((EnumerationObject) o2).getValue();
                  int v = 0;
                  int result = (s1.toLowerCase()).compareTo(s2.toLowerCase());
                  if (result < 0) v = -1;
                  else if (result > 0) v = 1;
                  return v;
              }
          };
          Collections.sort(enumerations, c);
      }
      
      /**
       * Starts an asynchronous call to load the photo of the currently
       * selected user.
       */
      private void fireExperimenterPhotoLoading()
      {
      	if (refObject instanceof ExperimenterData) {
      		ExperimenterData exp = (ExperimenterData) refObject;
      		if (usersPhoto == null || !usersPhoto.containsKey(exp.getId())) {
      			UserPhotoLoader loader = new UserPhotoLoader(component,
      					parent.getSecurityContext(), exp);
          		loader.load();
      		}
      	}
      }
      
  	/**
  	 * Returns the group corresponding to the specified id or <code>null</code>.
  	 * 
  	 * @param groupId The identifier of the group.
  	 * @return See above.
  	 */
  	private GroupData getGroup(long groupId)
  	{
  		Collection groups = MetadataViewerAgent.getAvailableUserGroups();
  		if (groups == null) return null;
  		Iterator i = groups.iterator();
  		GroupData group;
  		while (i.hasNext()) {
  			group = (GroupData) i.next();
  			if (group.getId() == groupId) return group;
  		}
  		return null;
  	}
  	
  	/**
  	 * Indicates to load all annotations available if the user can annotate
  	 * and is an administrator/group owner or to only load the user's
  	 * annotation.
  	 * 
  	 * @return See above
  	 */
  	private boolean canRetrieveAll()
  	{
  		if (!canAnnotate()) return false;
  		//check the group level
  		GroupData group = getGroup(getRefObjectGroupID());
  		if (group == null) return false;
  		switch (group.getPermissions().getPermissionsLevel()) {
  			case GroupData.PERMISSIONS_GROUP_READ:
  				if (MetadataViewerAgent.isAdministrator()) return true;
  				Set leaders = group.getLeaders();
  				Iterator i = leaders.iterator();
  				long userID = getCurrentUser().getId();
  				ExperimenterData exp;
  				while (i.hasNext()) {
  					exp = (ExperimenterData) i.next();
  					if (exp.getId() == userID)
  						return true;
  				}
  				return false;
  			case GroupData.PERMISSIONS_PRIVATE:
  				return false;
  		}
  		return true;
  	}
  
  	/**
  	 * Returns <code>true</code> if the object can be deleted,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @param result the object of reference.
  	 * @return See above.
  	 */
  	private boolean canDeleteLink(Object data, StructuredDataResults result)
  	{
  		if (!(data instanceof DataObject)) return false;
  		DataObject d = (DataObject) data;
  		if (result == null) return false;
  		Collection<AnnotationLinkData> links = result.getAnnotationLinks();
  		if (links == null) return false;
  		Iterator<AnnotationLinkData> i = links.iterator();
  		AnnotationLinkData link;
  		
  		while (i.hasNext()) {
  			link = i.next();
  			if (d.getId() == link.getChild().getId())
  				return link.canDelete();
  		}
  		return false;
  	}
  
  	/**
  	 * Creates a new instance.
  	 * 
  	 * @param refObject	The object this editor is for.
  	 * @param parent	The parent of this browser.
  	 */
  	EditorModel(Object refObject, MetadataViewer parent) 
  	{
  		if (refObject == null)
  			throw new IllegalArgumentException("No object set.");
  		this.parent = parent;
  		this.refObject = refObject;
  		loaders = new ArrayList<EditorLoader>();
  		sorter = new ViewerSorter();
  	}
  	
  	/**
  	 * Adds the annotation to the collection of annotation to be deleted.
  	 * 
  	 * @param annotation The value to add.
  	 */
  	void deleteAnnotation(AnnotationData annotation)
  	{
  		if (annotation == null) return;
  		if (toDelete == null) toDelete = new ArrayList<AnnotationData>();
  		toDelete.add(annotation);
  	}
  	
  	/**
  	 * Returns the collection of annotation to delete.
  	 * 
  	 * @return See above.
  	 */
  	List<AnnotationData> getAnnotationToDelete() { return toDelete; }
  	
  	/**
  	 * Returns <code>true</code> if multiple selection is on, 
  	 * <code>false</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean isMultiSelection() { return !parent.isSingleMode(); }
  
  	
  	/**
  	 * Returns the observable.
  	 * 
  	 * @return See above.
  	 */
  	ObservableComponent getObservable() { return parent; }
  	
  	/**
  	 * Sets the browser.
  	 * 
  	 * @param browser The value to set.
  	 */
  	void setBrowser(Browser browser) { this.browser = browser; }
  	
  	/**
  	 * Returns the browser.
  	 * 
  	 * @return See above.
  	 */
  	Browser getBrowser() { return browser; }
  	
  	/**
  	 * Called by the <code>Editor</code> after creation to allow this
  	 * object to store a back reference to the embedding component.
  	 * 
  	 * @param component The embedding component.
  	 */
  	void initialize(Editor component) { this.component = component; }
  	
  	/**
       * Returns <code>true</code> if the permissions can be shown,
       * <code>false</code> otherwise.
       * 
       * @return See above.
       */
  	boolean isPermissionsShowable()
  	{
  		Object ref = getRefObject();
  		if (ref == null) return false;
      	return ((ref instanceof ProjectData) || 
      			(ref instanceof DatasetData));
  	}
  
  	/**
       * Returns the permission of the currently edited object. 
       * Returns <code>null</code> if no permission associated. (This should never
       * happens).
       * 
       * @return See above.
       */
  	PermissionData getRefObjectPermissions()
  	{
  		Object ref = getRefObject();
  		if (ref instanceof DataObject)
  			return ((DataObject) ref).getPermissions();
  		return null;
  	}
  	
  	/**
  	 * Returns the name of the object if any.
  	 * 
  	 * @return See above.
  	 */
  	String getRefObjectName() 
  	{
  		return getObjectName(getPrimarySelect());
  	}
  	
  	/**
  	 * Returns the name of the specified object.
  	 * 
  	 * @param ref The object to handle.
  	 * @return See above.
  	 */
  	String getObjectName(Object ref)
  	{
  		String name = "";
  		if (ref instanceof ImageData)
  			name = ((ImageData) ref).getName();
  		else if (ref instanceof DatasetData)
  			name = ((DatasetData) ref).getName();
  		else if (ref instanceof ProjectData)
  			name = ((ProjectData) ref).getName();
  		else if (ref instanceof TagAnnotationData ||
  				ref instanceof TermAnnotationData)
  			name = ((AnnotationData) ref).getContentAsString();
  		else if (ref instanceof ScreenData)
  			name = ((ScreenData) ref).getName();
  		else if (ref instanceof PlateData)
  			name = ((PlateData) ref).getName();
  		else if (ref instanceof PlateAcquisitionData)
  			name = ((PlateAcquisitionData) ref).getLabel();
  		else if (ref instanceof FileAnnotationData)
  			name = ((FileAnnotationData) ref).getFileName();
  		else if (ref instanceof WellSampleData) {
  			WellSampleData ws = (WellSampleData) ref;
  			ImageData img = ws.getImage();
  			if (img != null && img.getId() >= 0) name = img.getName();
  		} else if (ref instanceof FileData)
  			name = ((FileData) ref).getName();
  		if (name == null) return "";
  		return name.trim();
  	}
  	
  	/**
  	 * Returns the name associated to the specified object.
  	 * 
  	 * @param ref The object to handle.
  	 * @return See above.
  	 */
  	String getObjectTypeAsString(Object ref)
  	{
  		if (ref instanceof ImageData) return "Image";
          else if (ref instanceof DatasetData) return "Dataset";
          else if (ref instanceof ProjectData) return "Project";
          else if (ref instanceof ScreenData) return "Screen";
          else if (ref instanceof PlateData) return "Plate";
          else if (refObject instanceof PlateAcquisitionData)
          	return"Plate Run";
          else if (refObject instanceof FileAnnotationData) {
          	return "File";
          } else if (refObject instanceof WellSampleData) return "Field";
          else if (refObject instanceof WellData) return "Well";
          else if (refObject instanceof TagAnnotationData) {
          	TagAnnotationData tag = (TagAnnotationData) refObject;
          	if (TagAnnotationData.INSIGHT_TAGSET_NS.equals(tag.getNameSpace()))
          		return "Tag Set";
          	else return "Tag";
          } else if (refObject instanceof FileData) {
          	FileData f = (FileData) refObject;
          	if (f.isDirectory()) return "Folder";
          	return "File";
          } else if (refObject instanceof TermAnnotationData) {
          	return "Term";
          } else if (refObject instanceof XMLAnnotationData) {
          	return "XML file";
          } else if (refObject instanceof LongAnnotationData ||
          		refObject instanceof DoubleAnnotationData) {
          	return "Numerical value";
          }
          else if (refObject instanceof BooleanAnnotationData) {
          	return "Boolean value";
          }
  		return "";
  	}
  	
  	/**
  	 * Returns the description of the object if any.
  	 * 
  	 * @return See above.
  	 */
  	String getRefObjectDescription()
  	{
  		String description = "";
  		Object ref = getPrimarySelect();
  		if (ref instanceof ImageData)
  			description = ((ImageData) ref).getDescription();
  		else if (ref instanceof DatasetData)
  			description = ((DatasetData) ref).getDescription();
  		else if (ref instanceof ProjectData)
  			description = ((ProjectData) ref).getDescription();
  		else if (ref instanceof ScreenData)
  			description = ((ScreenData) ref).getDescription();
  		else if (ref instanceof PlateData)
  			description = ((PlateData) ref).getDescription();
  		else if (ref instanceof PlateAcquisitionData)
  			description = ((PlateAcquisitionData) ref).getDescription();
  		else if (ref instanceof TagAnnotationData) {
  			description = ((TagAnnotationData) ref).getTagDescription();
  		}
  		else if (ref instanceof WellData) { description = ""; }
  		else if (ref instanceof WellSampleData) {
  			ImageData img = ((WellSampleData) ref).getImage();
  			if (!CommonsLangUtils.isEmpty(img.getDescription()))
  			    description = img.getDescription();
  		} else if (ref instanceof FileData) 
  			description = null;//((FileData) ref).getDescription();
  		if (description == null) return "";
  		return description.trim();
  	}
  	
  	/**
  	 * Returns the id of the reference object if it is an instance of 
  	 * <code>DataObject</code> or <code>-1</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	long getRefObjectID()
  	{
  		Object ref = getRefObject();
  		if (ref instanceof DataObject)
  			return ((DataObject) ref).getId();
  		return -1;
  	}
  	
  	/**
  	 * Returns the group's id of the reference object if it is an instance of 
  	 * <code>DataObject</code> or <code>-1</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	long getRefObjectGroupID()
  	{
  		Object ref = getRefObject();
  		if (ref instanceof DataObject)
  			return ((DataObject) ref).getGroupId();
  		return -1;
  	}
  	
  	/**
  	 * Returns <code>true</code> if the user currently logged in is
  	 * the one currently edited, <code>false</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean isSelf()
  	{
  		Object ref = getRefObject();
  		if (!(ref instanceof ExperimenterData)) return false;
  		ExperimenterData exp = getCurrentUser();
  		return exp.getId() == getRefObjectID();
  	}
  	
  	/**
  	 * Returns the <code>DataObject</code> this editor is for.
  	 * 
  	 * @return See above.
  	 */
  	Object getRefObject()
  	{ 
  		StructuredDataResults data = parent.getStructuredData();
  		if (data == null) return refObject;
  		return refObject; 
  	}
  	
  	/**
  	 * Returns the primary select object.
  	 * 
  	 * @return See above.
  	 */
  	Object getPrimarySelect()
  	{
  		if (!isMultiSelection()) return getRefObject();
  		List list = parent.getRelatedNodes();
  		if (CollectionUtils.isEmpty(list)) return getRefObject();
  		return list.get(0);
  	}
  	
  	/**
  	 * Returns <code>true</code> if the object can be edited,
  	 * <code>false</code> otherwise.
  	 *
  	 * @return See above.
  	 */
  	boolean canEdit() { return canEdit(refObject); }
  	
  	/**
  	 * Returns <code>true</code> if the object can be edited,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
  	boolean canEdit(Object data)
  	{
  	    if (!(data instanceof DataObject)) return false;
  	    DataObject d = (DataObject) data;
  	    return d.canEdit();
  	}
  	
  	/**
  	 * Returns <code>true</code> if the object can be linked,
  	 * <code>false</code> otherwise.
  	 *
  	 * @return See above.
  	 */
  	boolean canLink() { return canLink(refObject); }
  	
  	/**
  	 * Returns <code>true</code> if the object can be linked, e.g. 
  	 * image added to dataset, <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
  	boolean canLink(Object data)
  	{
  		switch (getDisplayMode()) {
  			case LookupNames.GROUP_DISPLAY:
  				if (data instanceof DataObject)
  					return ((DataObject) data).canLink();
  				return false;
  			case LookupNames.EXPERIMENTER_DISPLAY:
  			default:
  				return EditorUtil.isUserOwner(data, getLoggedInUserID());
  		}
  	}
  
  	/**
  	 * Returns <code>true</code> if the object can be annotated,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean canAnnotate() { return canAnnotate(refObject); }
  	
  	/**
  	 * Returns <code>true</code> if the object can be annotated,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
  	boolean canAnnotate(Object data)
  	{ 
  		if (!(data instanceof DataObject)) return false;
  		DataObject d = (DataObject) data;
  		return d.canAnnotate();
  	}
  	
  	/**
  	 * Returns <code>true</code> if the object can be deleted,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean canDelete() { return canDelete(refObject); }
  	
  	/**
  	 * Returns <code>true</code> if the object can be deleted,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
  	boolean canDelete(Object data)
  	{ 
  		if (!(data instanceof DataObject)) return false;
  		DataObject d = (DataObject) data;
  		return d.canDelete();
  	}
  	
  	/**
  	 * Returns <code>true</code> if the object can be deleted,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
  	boolean canDeleteLink(Object data)
  	{
  		Map<DataObject, StructuredDataResults> 
  		r = parent.getAllStructuredData();
  		if (r == null) return false;
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>>
  		i = r.entrySet().iterator();
  		while (i.hasNext()) {
  			e = i.next();
  			if (canDeleteLink(data, e.getValue()))
  				return true;
  		}
  		return false;
  	}
  
  	/**
  	 * Returns <code>true</code> if the object is a modulo annotation,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The data to handle.
  	 * @return See above.
  	 */
      boolean isModulo(Object data)
      {
          if (!(data instanceof XMLAnnotationData)) return false;
          //parse the annotation.
          XMLAnnotationData d = (XMLAnnotationData) data;
          ModuloParser parser = new ModuloParser(d.getText());
          try {
              parser.parse();
              return !CollectionUtils.isEmpty(parser.getModulos());
          } catch (Exception e) {
              LogMessage msg = new LogMessage();
              msg.append("Error while reading modulo annotation.");
              msg.print(e);
              MetadataViewerAgent.getRegistry().getLogger().error(this, msg);
          }
          return false;
      }
  	
  	/**
  	 * Returns <code>true</code> if the selected objects belong to several
  	 * groups, <code>false</code> otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean isAcrossGroups()
  	{
  		List<DataObject> l = getSelectedObjects();
  		if (l == null || l.size() == 0) return false;
  		List<Long> ids = new ArrayList<Long>();
  		Iterator<DataObject> i = l.iterator();
  		DataObject data;
  		while (i.hasNext()) {
  			data = i.next();
  			if (!ids.contains(data.getGroupId())) 
  				ids.add(data.getGroupId());
  		}
  		return ids.size() > 1;
  	}
  	
  	/**
  	 * Returns <code>true</code> if the annotation can be added, should
  	 * only be invoked for tagging or adding attachments, <code>false</code>
  	 * otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean canAddAnnotationLink()
  	{
  		if (!canAnnotate()) return false;
  		if (!isMultiSelection()) return true;
  		//multi selection.
  		List l = parent.getRelatedNodes();
  		if (l == null) return false;
  		Iterator i = l.iterator();
  		DataObject data;
  		List<Long> ids = new ArrayList<Long>();
  		while (i.hasNext()) {
  			data = (DataObject) i.next();
  			if (!(data instanceof GroupData || 
  					data instanceof ExperimenterData)) {
  				if (!ids.contains(data.getGroupId()))
  					ids.add(data.getGroupId());
  			}
  		}
  		return ids.size() <= 1;
  	}
  	
  	/**
  	 * Returns <code>true</code> if the annotation can be deleted, should
  	 * only be invoked for tagging or adding attachments, <code>false</code>
  	 * otherwise.
  	 * 
  	 * @return See above.
  	 */
  	boolean canDeleteAnnotationLink()
  	{
  		Map<DataObject, StructuredDataResults> 
  		r = parent.getAllStructuredData();
  		if (r == null) return false;
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>>
  		i = r.entrySet().iterator();
  		StructuredDataResults data;
  		Collection<AnnotationLinkData> links;
  		Iterator<AnnotationLinkData> j;
  		while (i.hasNext()) {
  			e = i.next();
  			data = e.getValue();
  			links = data.getAnnotationLinks();
  			if (links != null) {
  				j = links.iterator();
  				while (j.hasNext())
  					if ( j.next().canDelete()) return true;
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Returns <code>true</code> if the currently logged in user is the 
  	 * owner of the object, <code>false</code> otherwise.
  	 * 
  	 * @param object The object to handle.
  	 * @return See above.
  	 */
  	boolean isUserOwner(Object object)
  	{
  		long id = getLoggedInUserID();
  		if (object == null) return false;
  		if (object instanceof ExperimenterData) 
  			return (((ExperimenterData) object).getId() == id);
  		if (!(object instanceof DataObject)) return false;
  		if (object instanceof FileData || object instanceof ImageData) {
  			DataObject f = (DataObject) object;
  			if (f.getId() < 0) return id == getLoggedInUserID();
  		} 
  		return EditorUtil.isUserOwner(object, id);
  	}
  	
  	/**
  	 * Returns <code>true</code> if the current user is the owner of the link
  	 * between the passed annotation and the currently selected object.
  	 * 
  	 * @param annotation The annotation to handle.
  	 * @return See above.
  	 */
  	boolean isLinkOwner(Object annotation)
  	{
  		Map<DataObject, StructuredDataResults> data = getAllStructuredData();
  		if (data == null) return false;
  		
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>> 
  		j = data.entrySet().iterator();
  		
  		Iterator<AnnotationLinkData> i;
  		Collection<AnnotationLinkData> links;
  		AnnotationLinkData link;
  		DataObject ann = (DataObject) annotation;
  		
  		long id = getLoggedInUserID();
  		
  		while (j.hasNext()) {
  			e = j.next();
  			links = e.getValue().getAnnotationLinks();
  			if (links != null) {
  				i = links.iterator();
  				while (i.hasNext()) {
  					link = i.next();
  					if (link.getChild().getId() == ann.getId() &&
  						link.getOwner().getId() == id) {
  						return true;
  					}
  				}
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * Returns <code>true</code> if the annotation  has been added by others,
  	 * <code>false</code> otherwise.
  	 * 
  	 * @param data The annotation to handle.
  	 * @return See above.
  	 */
  	boolean isAnnotatedByOther(DataObject data)
  	{
  		if (data == null) return false;
  		
  		List<ExperimenterData> annotators = getAnnotators(data);
  		if (annotators == null || annotators.size() == 0) return false;
  		if (annotators.size() == 1) {
  			ExperimenterData exp = annotators.get(0);
  			long id = getLoggedInUserID();
  			return exp.getId() != id;
  		}
  		return true;
  	}
  	
  	/**
  	 * Returns the collection of experimenters who use the annotation.
  	 * 
  	 * @param annotation 	The annotation to handle.
  	 * @return See above.
  	 */
  	List<ExperimenterData> getAnnotators(Object annotation)
  	{
  		return getAnnotators(null, annotation);
  	}
  	
  	/**
  	 * Returns the collection of experimenters who use the annotation.
  	 * 
  	 * @param ref The object of reference.
  	 * @param annotation The annotation to handle.
  	 * @return See above.
  	 */
  	List<ExperimenterData> getAnnotators(DataObject ref, Object annotation)
  	{
  		List<ExperimenterData> list = new ArrayList<ExperimenterData>();
  		Map<DataObject, StructuredDataResults> data = getAllStructuredData();
  		if (data == null) return list;
  		
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>> 
  		j = data.entrySet().iterator();
  		
  		Iterator<AnnotationLinkData> i;
  		Collection<AnnotationLinkData> links;
  		AnnotationLinkData link;
  		DataObject ann = (DataObject) annotation;
  		
  		List<Long> ids = new ArrayList<Long>();
  		while (j.hasNext()) {
  			e = j.next();
  			if (ref == null) {
  				links = e.getValue().getAnnotationLinks();
  				if (links != null) {
  					i = links.iterator();
  					while (i.hasNext()) {
  						link = i.next();
  						if (link.getChild().getId() == ann.getId()) {
  							
  							if (!ids.contains(link.getOwner().getId())) {
  								list.add(link.getOwner());
  								ids.add(link.getOwner().getId());
  							}
  						}
  					}
  				}
  			} else {
  				if (ref.getId() == e.getKey().getId()) {
  					links = e.getValue().getAnnotationLinks();
  					if (links != null) {
  						i = links.iterator();
  						while (i.hasNext()) {
  							link = i.next();
  							if (link.getChild().getId() == ann.getId()) {
  								
  								if (!ids.contains(link.getOwner().getId())) {
  									list.add(link.getOwner());
  									ids.add(link.getOwner().getId());
  								}
  							}
  						}
  					}
  					break;
  				}
  			}
  			
  		}
  		return list;
  	}
  	
  	/**
  	 * Returns the links corresponding to the level and the annotation.
  	 * 
  	 * @param level The level to handle.
  	 * @param ho The annotation.
  	 * @return
  	 */
  	List<Object> getLinks(int level, AnnotationData ho)
  	{
  		Map<DataObject, StructuredDataResults> data = getAllStructuredData();
  		if (data == null) return null;
  		
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>> 
  		j = data.entrySet().iterator();
  		
  		Iterator<AnnotationLinkData> i;
  		AnnotationLinkData d;
  		List<Object> results = new ArrayList<Object>();
  		long userID = getCurrentUser().getId();
  		Collection<AnnotationLinkData> links;
  		while (j.hasNext()) {
  			e = j.next();
  			links = e.getValue().getAnnotationLinks();
  			if (links != null) {
  				i = links.iterator();
  				switch (level) {
  					case ALL:
  						while (i.hasNext()) {
  							d = i.next();
  							if (ho.getId() == d.getChild().getId()) {
  								results.add(d.getLink());
  							}
  						}
  						break;
  					case ME:
  						while (i.hasNext()) {
  							d = i.next();
  							if (ho.getId() == d.getChild().getId() &&
  									userID == d.getOwner().getId()) {
  								results.add(d.getLink());
  							}
  						}
  						break;
  					case OTHER:
  						while (i.hasNext()) {
  							d = i.next();
  							if (ho.getId() == d.getChild().getId() &&
  									userID != d.getOwner().getId()) {
  								results.add(d.getLink());
  							}
  						}
  				}
  			}
  			
  		}
  		return results;
  	}
  	
  	/**
  	 * Returns the FileAnnotations corresponding to the given level.
  	 * 
  	 * @param level The level to handle.
  	 * @return See above.
  	 */
          List<FileAnnotationData> getFileAnnotatationsByLevel(int level) {
              List<FileAnnotationData> result = new ArrayList<FileAnnotationData>();
              Collection<FileAnnotationData> all = getAllAttachments();
              for (FileAnnotationData f : all) {
                  switch (level) {
                      case ALL:
                          result.add(f);
                          break;
                      case ME:
                          if (MetadataViewerAgent.getUserDetails().getId() == f.getOwner().getId()) {
                              result.add(f);
                          }
                          break;
                      case OTHER:
                          if (MetadataViewerAgent.getUserDetails().getId() != f.getOwner().getId()) {
                              result.add(f);
                          }
                  }
              }
              return result;
          }
  
  	/**
  	 * Returns <code>true</code> if the annotation is already used by the 
  	 * current user, <code>false</code> otherwise.
  	 * 
  	 * @param annotation The annotation to handle.
  	 * @return See above.
  	 */
  	boolean isAnnotationUsedByUser(Object annotation)
  	{
  		Map<DataObject, StructuredDataResults> data = getAllStructuredData();
  		if (data == null) return false;
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>> 
  		j = data.entrySet().iterator();
  		Collection<AnnotationLinkData> links;
  		Iterator<AnnotationLinkData> i;
  		AnnotationLinkData link;
  		
  		long id = MetadataViewerAgent.getUserDetails().getId();
  		DataObject ann = (DataObject) annotation;
  		
  		while (j.hasNext()) {
  			e = j.next();
  			links = e.getValue().getAnnotationLinks();
  			i = links.iterator();
  			while (i.hasNext()) {
  				link = i.next();
  				if (link.getChild().getId() == ann.getId() &&
  					link.getOwner().getId() == id)
  					return true;
  			}
  			
  		}
  		return false;
  	}
  	
  	/**
  	 * Returns <code>true</code> if the user currently logged in, is a leader
  	 * of the selected group, <code>false</code> otherwise.
  	 * 
  	 * @return
  	 */
  	boolean isAdministrator()
  	{
  		return MetadataViewerAgent.isAdministrator();
  	}
  
      /**
       * Returns <code>true</code> if the user currently logged in, is a leader
       * of their default group, <code>false</code> otherwise.
       */
      boolean isGroupLeader()
      {
          return isGroupLeader(MetadataViewerAgent.getUserDetails().getDefaultGroup());
      }
  
  	/**
  	 * Returns <code>true</code> if the user currently logged in, is a leader
  	 * of the given group, <code>false</code> otherwise.
  	 */
  	boolean isGroupLeader(GroupData group)
  	{
  		ExperimenterData exp = MetadataViewerAgent.getUserDetails();
  		Collection groups = MetadataViewerAgent.getAvailableUserGroups();
  		if (groups == null) return false;
  		final long groupID = group.getId();
  		Iterator i = groups.iterator();
  		GroupData g;
  		Set leaders = null;
  		while (i.hasNext()) {
  			g = (GroupData) i.next();
  			if (g.getId() == groupID) {
  				leaders = g.getLeaders();
  				break;
  			}
  		}
  		if (leaders == null) return false;
  		i = leaders.iterator();
  		ExperimenterData data;
  		while (i.hasNext()) {
  			data = (ExperimenterData) i.next();
  			if (data.getId() == exp.getId()) return true;
  		}
  		return false;
  	}
  	
  	/**
  	 * Returns the first name and the last name of the owner.
  	 * 
  	 * @param object The object to handle.
  	 * @return See above.
  	 */
  	String formatOwner(DataObject object)
  	{
  		if (object == null) return "";
  		if (object instanceof ExperimenterData) {
  			return EditorUtil.formatExperimenter((ExperimenterData) object);
  		}
  		return formatOwner(object.getOwner());
  	}
  	
  	/**
  	 * Returns the date.
  	 * 
  	 * @param object The object to handle.
  	 * @return See above.
  	 */
  	String formatDate(DataObject object)
  	{ 
  		return EditorUtil.formatDate(object);
  	}
  	
  	/**
  	 * Returns the number of URLs linked to the <code>DataObject</code>.
  	 * 
  	 * @return See above.
  	 */
  	int getTermsCount()
  	{ 
  		Collection<TermAnnotationData> terms = getTerms();
  		if (terms == null) return 0;
  		return terms.size();
  	}
  	
  	/**
  	 * Returns the collection of the URLs linked to the <code>DataObject</code>.
  	 * 
  	 * @return See above.
  	 */
  	Collection<TermAnnotationData> getTerms()
  	{ 
  		StructuredDataResults data = parent.getStructuredData();
  		if (data == null) return null;
  		return data.getTerms(); 
  	}
  	
  	/**
  	 * Returns the number of tags linked to the <code>DataObject</code>.
  	 * 
  	 * @return See above.
  	 */
  	int getTagsCount()
  	{
  		Collection tags = getTags();
  		if (tags == null) return 0;
  		return tags.size();
  	}
  	
  	/**
  	 * Returns the collection of the tags linked to the <code>DataObject</code>.
  	 * 
  	 * @return See above.
  	 */
  	Collection<TagAnnotationData> getTags()
  	{ 
  		StructuredDataResults data = parent.getStructuredData();
  		if (data == null) return new ArrayList<TagAnnotationData>();
  		Collection<TagAnnotationData> tags = data.getTags();
  		if (tags == null || tags.size() == 0) 
  			return new ArrayList<TagAnnotationData>();
  		return (Collection<TagAnnotationData>) sorter.sort(tags);
  	}
  	
  	/**
  	 * Returns the collection of the tags linked to the
  	 * <code>DataObject</code>s.
  	 * 
  	 * @return See above.
  	 */
  	Collection<TagAnnotationData> getAllTags()
  	{
  		Map<DataObject, StructuredDataResults> 
  		r = parent.getAllStructuredData();
  		if (r == null) return new ArrayList<TagAnnotationData>();
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>>
  		i = r.entrySet().iterator();
  		Collection<TagAnnotationData> tags;
  		List<TagAnnotationData> results = new ArrayList<TagAnnotationData>();
  		List<Long> ids = new ArrayList<Long>();
  		Iterator<TagAnnotationData> j;
  		TagAnnotationData tag;
  		while (i.hasNext()) {
  			e = i.next();
  			tags = e.getValue().getTags();
  			if (tags != null) {
  				j = tags.iterator();
  				while (j.hasNext()) {
  					tag = j.next();
  					if (!ids.contains(tag.getId())) {
  						results.add(tag);
  						ids.add(tag.getId());
  					}
  				}
  			}
  		}
  		return (Collection<TagAnnotationData>) sorter.sort(results);
  	}
  	
  	/**
  	 * Returns the collection of the tags that are linked to all the selected
  	 * objects.
  	 * 
  	 * @return See above.
  	 */
  	Collection<TagAnnotationData> getCommonTags()
  	{
  		Map<DataObject, StructuredDataResults> 
  		r = parent.getAllStructuredData();
  		if (r == null) return new ArrayList<TagAnnotationData>();
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>>
  		i = r.entrySet().iterator();
  		Collection<TagAnnotationData> tags;
  		Map<Long, Integer> 
  			ids = new HashMap<Long, Integer>();
  		Iterator<TagAnnotationData> j;
  		TagAnnotationData tag;
  		
  		Integer value;
  		while (i.hasNext()) {
  			e = i.next();
  			tags = e.getValue().getTags();
  			if (tags != null) {
  				j = tags.iterator();
  				while (j.hasNext()) {
  					tag = j.next();
  					value = ids.get(tag.getId());
  					if (value != null) {
  						value++;
  					} else value = 1;
  					ids.put(tag.getId(), value);
  				}
  			}
  		}
  		
  		//Extract the common tags.
  		//The number of selected objects.
  		List<TagAnnotationData> results = new ArrayList<TagAnnotationData>();
  		List<Long> count = new ArrayList<Long>();
  		
  		int max = r.size();
  		i = r.entrySet().iterator();
  		while (i.hasNext()) {
  			e = i.next();
  			tags = e.getValue().getTags();
  			if (tags != null) {
  				j = tags.iterator();
  				while (j.hasNext()) {
  					tag = j.next();
  					value = ids.get(tag.getId());
  					if (value != null &&
  						value == max && !count.contains(tag.getId())) {
  						results.add(tag);
  						count.add(tag.getId());
  					}
  				}
  			}
  		}
  		
  		return (Collection<TagAnnotationData>) sorter.sort(results);
  	}
  	
  	/**
  	 * Returns the objects tagged by the specified object.
  	 * 
  	 * @param refTag The tag of reference.
  	 * @return See above.
  	 */
  	Map<DataObject, Boolean> getTaggedObjects(AnnotationData refTag)
  	{
  		Map<DataObject, StructuredDataResults> 
  		r = parent.getAllStructuredData();
  		Map<DataObject, Boolean> m = new HashMap<DataObject, Boolean>();
  		if (r == null) return m;
  		Entry<DataObject, StructuredDataResults> e;
  		Iterator<Entry<DataObject, StructuredDataResults>>
  