/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package omero.gateway.model; import static omero.rtypes.rlong; import java.sql.Timestamp; import java.util.ArrayList; 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 ome.model.IAnnotated; import ome.model.IMutable; import omero.model.Annotation; import omero.model.BooleanAnnotation; import omero.model.Channel; import omero.model.CommentAnnotation; import omero.model.Dataset; import omero.model.Details; import omero.model.Event; import omero.model.Experimenter; import omero.model.ExperimenterGroup; import omero.model.FileAnnotation; import omero.model.IObject; import omero.model.Image; import omero.model.Length; import omero.model.LengthI; import omero.model.LongAnnotation; import omero.model.Permissions; import omero.model.Pixels; import omero.model.Plate; import omero.model.Project; import omero.model.Screen; import omero.model.TagAnnotation; import omero.model.TermAnnotation; import omero.model.Well; import omero.model.WellSample; /** * Abstract superclass for objects that hold <i>OMEDS</i> data. Delegates * getters and setters to <code>IObject</code> instances. Modifications are * propagated. <b>Not thread-safe.</b> * * @author Jean-Marie Burel      <a * href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br> * Andrea Falconi      <a * href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a> * @author <br> * Josh Moore      <a * href="mailto:josh.more@gmx.de"> josh.moore@gmx.de</a> * @version 2.2 <small> (<b>Internal version:</b> $Revision$ $Date$) </small> * @since OME2.2 */ public abstract class DataObject { /** * Converts the collection of {@link IObject}s into the corresponding * {@link DataObject}s * * @param iObjects * The map to handle. * @return See above. */ public static Set asPojos(Collection iObjects) { Set<DataObject> result = new HashSet<DataObject>(); IObject obj; for (Iterator it = iObjects.iterator(); it.hasNext();) { obj = (IObject) it.next(); result.add(asPojo(obj)); } return result; } /** * Converts the map of {@link IObject}s into the corresponding * {@link DataObject}s * * @param iObjects * The map to handle. * @return See above. */ public static Map asPojos(Map iObjects) { Map result = new HashMap(); Object key, value; Object convertedKey, convertedValue; for (Iterator it = iObjects.keySet().iterator(); it.hasNext();) { key = it.next(); value = iObjects.get(key); convertedKey = null; convertedValue = null; if (key instanceof IObject) { convertedKey = asPojo((IObject) key); } else if (key instanceof Collection) { convertedKey = asPojos((Collection) key); } else if (key instanceof Map) { convertedKey = asPojos((Map) key); } if (value instanceof IObject) { convertedValue = asPojo((IObject) iObjects.get(key)); } else if (value instanceof Collection) { convertedValue = asPojos((Collection) iObjects.get(key)); } else if (value instanceof Map) { convertedValue = asPojos((Map) iObjects.get(key)); } result.put(null == convertedKey ? key : convertedKey, null == convertedValue ? value : convertedValue); } return result; } /** * Converts the passed {@link IObject} into its corresponding Pojo object. * * @param obj * The object to convert. * @return See above. */ public static DataObject asPojo(IObject obj) { DataObject converted = null; if (obj instanceof Project) { converted = new ProjectData((Project) obj); } else if (obj instanceof Dataset) { converted = new DatasetData((Dataset) obj); } else if (obj instanceof TermAnnotation) { converted = new TermAnnotationData((TermAnnotation) obj); } else if (obj instanceof TagAnnotation) { converted = new TagAnnotationData((TagAnnotation) obj); } else if (obj instanceof CommentAnnotation) { converted = new TextualAnnotationData((CommentAnnotation) obj); } else if (obj instanceof LongAnnotation) { LongAnnotation ann = (LongAnnotation) obj; if (RatingAnnotationData.INSIGHT_RATING_NS.equals(ann.getNs().getValue())) { converted = new RatingAnnotationData(ann); } else { converted = new LongAnnotationData(ann); } } else if (obj instanceof BooleanAnnotation) { converted = new BooleanAnnotationData((BooleanAnnotation) obj); } else if (obj instanceof FileAnnotation) { converted = new FileAnnotationData((FileAnnotation) obj); } else if (obj instanceof Image) { converted = new ImageData((Image) obj); } else if (obj instanceof Pixels) { converted = new PixelsData((Pixels) obj); } else if (obj instanceof Experimenter) { converted = new ExperimenterData((Experimenter) obj); } else if (obj instanceof ExperimenterGroup) { converted = new GroupData((ExperimenterGroup) obj); } else if (obj != null) { throw new IllegalArgumentException("Unknown type: " + obj.getClass().getName()); } return converted; } /** delegate IObject */ private IObject value = null; /** lazily-loaded owner */ private ExperimenterData owner = null; /** lazily-loaded permissions */ private PermissionData permissions = null; /** * NOTE: IObject-views are mutable. we can't ensure non-dirtiness, only * non-cleanness */ private boolean dirty = false; /** * Sets the {@link IObject}. * * @param value * The value to set. */ protected void setValue(IObject value) { if (value == null) { throw new IllegalArgumentException( "IObject delegate for DataObject cannot be null."); } this.value = value; } /** * Returns <code>true</code> if setter value has modified the value of the * stored IObject, <code>false</code> otherwise. * * @return See above. */ public boolean isDirty() { return dirty; } /** * Sets to <code>true</code> if the value has been modified, <false> * otherwise. * * @param dirty * The value to set. */ protected void setDirty(boolean dirty) { this.dirty = dirty; } // Common properties /** * Returns the database id of the IObject or <code>-1</code> if * <code>null</code> * * @return See above. */ public long getId() { return value.getId() == null ? -1 : (value.getId() == null ? -1 : value .getId().getValue()); } /** * Sets the database id. * * @param id * The value to set. */ public void setId(long id) { setDirty(true); value.setId(rlong(id)); } /** * Returns the version of the object if the object is immutable, * <code>false</code> otherwise. * * @return See above. */ protected int getVersion() { if (value instanceof IMutable) { IMutable m = (IMutable) value; return m.getVersion() == null ? 0 : m.getVersion().intValue(); } return 0; } /** * Sets the version of the object if it is immutable. * * @param version * The value to set. */ protected void setVersion(int version) { if (value instanceof IMutable) { IMutable m = (IMutable) value; setDirty(true); m.setVersion(new Integer(version)); } } /** * Returns <code>true</code> if the object is loaded, <code>false</code> * otherwise. * * @return See above. */ public boolean isLoaded() { return value.isLoaded(); } /** * Returns the owner of the object. * * @return See above. */ public ExperimenterData getOwner() { if (owner == null) { Experimenter experimenter = asIObject().getDetails().getOwner(); owner = experimenter != null ? new ExperimenterData(experimenter) : null; } return owner; } /** * Returns the permission of the object. * * @return See above. */ public PermissionData getPermissions() { if (permissions == null) { permissions = new PermissionData(getDetails().getPermissions()); } return permissions; } /** * Overridden to return the name of the class and the object id. * * @see Object#toString() */ @Override public String toString() { return getClass().getName() + " (id=" + getId() + ")"; } // ~ Helpers // ========================================================================= protected int nullSafe(Integer i) { return i == null ? 0 : i.intValue(); } protected int nullSafe(omero.RInt i) { return i == null ? 0 : i.getValue(); } protected long nullSafe(Long l) { return l == null ? 0L : l.longValue(); } protected double nullSafe(Double d) { return d == null ? 0.0 : d.doubleValue(); } protected float nullSafe(Float f) { return f == null ? 0.0f : f.floatValue(); } protected float nullSafe(omero.RFloat f) { return f == null ? 0.0f : f.getValue(); } protected double nullSafe(omero.RDouble d) { return d == null ? 0.0d : d.getValue(); } protected double nullSafe(omero.model.Length l) { return l == null ? 0.0d : l.getValue(); } protected Timestamp timeOfEvent(Event event) { if (event == null || !event.isLoaded() || event.getTime() == null) { return null; } return new Timestamp(event.getTime().getValue()); } /** * Returns <code>true</code> if the details are <code>null</code> * otherwise <code>false</code> otherwise. * * @return See above. */ protected boolean nullDetails() { return asIObject().getDetails() == null; } /** * Returns the details of the object. * * @return See above. */ protected Details getDetails() { return asIObject().getDetails(); } /** * Returns the creation time of the object. * * @return See above. */ public Timestamp getCreated() { if (nullDetails()) return null; return timeOfEvent(getDetails().getCreationEvent()); } /** * Returns the updated time of the object. * * @return See above. */ public Timestamp getUpdated() { if (nullDetails()) return null; return timeOfEvent(getDetails().getUpdateEvent()); } /** * Returns the id of the group. * * @return See above. */ public long getGroupId() { Details d = getDetails(); if (d == null) return -1; if (d.getGroup() == null) return -1; return d.getGroup().getId().getValue(); } // ~ VIEWS // ========================================================================= // These methods should never a null value // since single setter checks for null. /** * not null; no exceptions. * * @return not null IObject */ public IObject asIObject() { return value; } /** * not null; may through class-cast exception * * @throws ClassCastException * @return not null IObject */ public IAnnotated asIAnnotated() { return (IAnnotated) asIObject(); } /** * Returns the hosted IObject as an Experimenter. Not null; may through * class-cast exception * * @throws ClassCastException * @return not null IObject */ public Experimenter asExperimenter() { return (Experimenter) asIObject(); } /** * Returns the hosted IObject as an Experimenter Group. Not null; may * through class-cast exception * * @throws ClassCastException * @return not null IObject */ public ExperimenterGroup asGroup() { return (ExperimenterGroup) asIObject(); } /** * Returns the hosted IObject as an Annotation. Not null; may through * class-cast exception * * @throws ClassCastException * @return not null IObject */ public Annotation asAnnotation() { return (Annotation) asIObject(); } /** * Returns the hosted IObject as an Image. Not null; may through class-cast * exception * * @throws ClassCastException * @return not null IObject */ public Image asImage() { return (Image) asIObject(); } /** * Returns the hosted IObject as a Dataset. Not null; may through class-cast * exception * * @throws ClassCastException * @return not null IObject */ public Dataset asDataset() { return (Dataset) asIObject(); } /** * Returns the hosted IObject as a Project. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Project asProject() { return (Project) asIObject(); } /** * Returns the hosted IObject as a Pixels. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Pixels asPixels() { return (Pixels) asIObject(); } /** * Returns the hosted IObject as a Screen. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Screen asScreen() { return (Screen) asIObject(); } /** * Returns the hosted IObject as a Plate. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Plate asPlate() { return (Plate) asIObject(); } /** * Returns the hosted IObject as a Well. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Well asWell() { return (Well) asIObject(); } /** * Returns the hosted IObject as a Well. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public WellSample asWellSample() { return (WellSample) asIObject(); } /** * Returns the hosted IObject as a Well. Not null; may through class-cast * exception * * @throws ClassCastException * @return See above */ public Channel asChannel() { return (Channel) asIObject(); } /** * Returns <code>true</code> if the object can be annotated * <code>false</code> otherwise, depending on permissions level. * * @return See above. */ public boolean canAnnotate() { Permissions p = asIObject().getDetails().getPermissions(); if (p == null) return false; return p.canAnnotate(); } /** * Returns <code>true</code> if the object can be edited by the user * currently logged in <code>false</code> otherwise, * depending on permissions level. * * @return See above. */ public boolean canEdit() { Permissions p = asIObject().getDetails().getPermissions(); if (p == null) return false; return p.canEdit(); } /** * Returns <code>true</code> if the object can be linked e.g. image * add to dataset, by the user currently logged in, * <code>false</code> otherwise, depending on * permissions level. * * @return See above. */ public boolean canLink() { Permissions p = asIObject().getDetails().getPermissions(); if (p == null) return false; return p.canLink(); } /** * Returns <code>true</code> if the object can be deleted by the user * currently logged in, * <code>false</code> otherwise, depending on permissions level. * * @return See above. */ public boolean canDelete() { Permissions p = asIObject().getDetails().getPermissions(); if (p == null) return false; return p.canDelete(); } } /** * Encapsulates the logic to update one set based on the changes made to a copy * of that set. Provides two iterators for acting on the changes: <code> * SetMutator m = new SetMutator( oldSet, updatedSet ); * while ( m.hasDeletions() ) * { * DataObject d = m.nextDeletion(); * } * * while ( m.hasAdditions() ) * { * DataObject d = m.nextAddition(); * } * return m.result(); * </code> */ class SetMutator<E> { private final List<E> _old, _new, del, add; private final Iterator<E> r, a; /** null-safe constructor */ public SetMutator(Collection<E> originalSet, Collection<E> targetSet) { _old = originalSet == null ? new ArrayList<E>() : new ArrayList<E>( originalSet); _new = targetSet == null ? new ArrayList<E>() : new ArrayList<E>( targetSet); del = new ArrayList<E>(_old); del.removeAll(_new); r = del.iterator(); add = new ArrayList<E>(_new); add.removeAll(_old); a = add.iterator(); } public boolean moreDeletions() { return r.hasNext(); } public DataObject nextDeletion() { return (DataObject) r.next(); } public boolean moreAdditions() { return a.hasNext(); } public DataObject nextAddition() { return (DataObject) a.next(); } public List<E> result() { return new ArrayList<E>(_new); } }