/* * $Id$ * * Copyright 2006 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.model.internal; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.persistence.MappedSuperclass; import javax.persistence.Transient; import ome.model.IObject; import ome.model.meta.Event; import ome.model.meta.Experimenter; import ome.model.meta.ExperimenterGroup; import ome.model.meta.ExternalInfo; import ome.util.Filter; import ome.util.Filterable; /** * value type for low-level (row-level) details for all * {@link ome.model.IObject} objects. Details instances are given special * treatment through the Omero system, especially during * {@code ome.api.IUpdate} update. * * @author Josh Moore      <a * href="mailto:josh.moore@gmx.de">josh.moore@gmx.de</a> * @version 3.0 * @since 3.0 * @see "ome.api.IUpdate" */ @MappedSuperclass public abstract class Details implements Filterable, Serializable { private static final long serialVersionUID = 1176546684904748977L; /** * Call {@link #create(Object[])} with null. */ public final static Details create() { return create(null); } /** * Factory method which returns a {@link Details} implementation for passing * in API calls, but cannot be stored within an {@link IObject} instance. * Use {@link #copy(Details)} or {@link #shallowCopy(Details)} instead. */ public final static Details create(final Object[] contexts) { return new Details(contexts) { private static final long serialVersionUID = Details.serialVersionUID; @Override public Details newInstance() { return Details.create(contexts); } }; } public final static String PERMISSIONS = "Details_permissions"; public final static String EXTERNALINFO = "Details_externalInfo"; public final static String CREATIONEVENT = "Details_creationEvent"; public final static String OWNER = "Details_owner"; public final static String GROUP = "Details_group"; public final static String UPDATEEVENT = "Details_updateEvent"; protected Event _update; protected IObject _context; protected Permissions _perms; protected ExternalInfo _externalInfo; protected Event _creation; protected Experimenter _owner; protected ExperimenterGroup _group; @Transient protected Set<String> _filteredCollections; @Transient protected Map _dynamicFields; @Transient protected Object[] contexts; /** default constructor. Leaves values null to save resources. */ public Details() { this((Object[])null); } public Details(Object[] contexts) { this.contexts = contexts; } /** copy-constructor */ public Details(Details copy) { this(); copy(copy); } public Object contextAt(int i) { if (contexts == null || i >= contexts.length) { return null; } return contexts[i]; } public void setContexts(Object[] contexts) { this.contexts = contexts; } public Details shallowCopy() { Details d = newInstance(); d.shallowCopy(this); return d; } public Details copy() { Details d = newInstance(); d.copy(this); return d; } /** * Method which takes all field values from the given {@link Details} * instance and copies them into the current instance. * * @param copy */ public void copy(Details copy) { if (copy == null) { throw new IllegalArgumentException("argument may not be null"); } setContexts(copy.contexts); setContext(copy.getContext()); possiblySetPermissions(copy); setExternalInfo(copy.getExternalInfo()); setCreationEvent(copy.getCreationEvent()); setOwner(copy.getOwner()); setGroup(copy.getGroup()); setUpdateEvent(copy.getUpdateEvent()); // Non-entity fields _filteredCollections = copy.filteredSet(); } private void possiblySetPermissions(Details copy) { Permissions p = null; Permissions copy_p = copy.getPermissions(); if (copy_p != null) { p = new Permissions(copy_p); } setPermissions(p); } /** * For any field of this which is null (and is NOT null on mask -- assuming * mask is not null), copy the same value from copyFrom * into this object. * @param mask * @param copyFrom */ public void copyWhereUnset(Details mask, Details copyFrom) { if (copyFrom == null) { throw new IllegalArgumentException("argument may not be null"); } boolean skipMask = (mask == null); if (getContext() == null && (skipMask || mask.getContext() != null)) { setContext(copyFrom.getContext()); } if (getPermissions() == null && (skipMask || mask.getPermissions() != null)) { possiblySetPermissions(copyFrom); } if (getCreationEvent() == null && (skipMask || mask.getCreationEvent() != null)) { setCreationEvent(copyFrom.getCreationEvent()); } if (getOwner() == null && (skipMask || mask.getOwner() != null)) { setOwner(copyFrom.getOwner()); } if (getGroup() == null && (skipMask || mask.getGroup() != null)) { setGroup(copyFrom.getGroup()); } if (getUpdateEvent() == null && (skipMask || mask.getUpdateEvent() != null)) { setUpdateEvent(copyFrom.getUpdateEvent()); } } /** * Method which takes all the fields of the given {@link Details} instance * and sets unloaded proxies of them into the current instance. */ public void shallowCopy(Details copy) { if (copy == null) { throw new IllegalArgumentException("argument may not be null"); } setOwner(copy.getOwner() == null ? null : new Experimenter(copy .getOwner().getId(), false)); setGroup(copy.getGroup() == null ? null : new ExperimenterGroup(copy .getGroup().getId(), false)); setCreationEvent(copy.getCreationEvent() == null ? null : new Event( copy.getCreationEvent().getId(), false)); setPermissions(copy.getPermissions() == null ? null : new Permissions(copy.getPermissions())); setExternalInfo(copy.getExternalInfo() == null ? null : new ExternalInfo(copy.getExternalInfo().getId(), false)); setUpdateEvent(copy.getUpdateEvent() == null ? null : new Event(copy .getUpdateEvent().getId(), false)); _filteredCollections = copy.filteredSet(); } // Loaded&Filtering methods // =========================================================== /** * consider the collection named by <code>collectionName</code> to be a * "filtered" representation of the DB. This collection should not be saved, * at most compared with the current DB to find <em>added</em> entities. */ public void addFiltered(String collectionName) { if (_filteredCollections == null) { _filteredCollections = new HashSet<String>(); } _filteredCollections.add(collectionName); } /** * consider all the collections named by the elements of collection to be a * "filtered" representation of the DB. This collection should not be saved, * at most compared with the current DB to find <em>added</em> entities. */ public void addFiltered(Collection<String> collection) { if (_filteredCollections == null) { _filteredCollections = new HashSet<String>(); } _filteredCollections.addAll(collection); } /** * Was this collection filtered during creation? If so, it should not be * saved to the DB. */ public boolean isFiltered(String collectionName) { if (_filteredCollections == null) { return false; } if (_filteredCollections.contains(collectionName)) { return true; } return false; } /** * all currently marked collections are released. The space taken up by the * collection is also released. */ public void clearFiltered() { _filteredCollections = null; } /** * the count of collections which were filtered. * * @return number of String keys in the filtered set. */ public int filteredSize() { if (_filteredCollections == null) { return 0; } return _filteredCollections.size(); } /** * copy of the current collection of filtered names. Changes to this * collection are not propagated. * * @return filtered set copy. */ public Set<String> filteredSet() { if (_filteredCollections == null) { return new HashSet<String>(); } return new HashSet<String>(_filteredCollections); } // ~ Other // =========================================================== /** * reference to the entity which this Details is contained in. This value is * <em>not</em> maintained by the backend but is an internal mechanism. */ // @Parent // TODO @Transient public IObject getContext() { return _context; } /** * set entity to which this Details belongs. This may cause erratic behavior * if called improperly. * * @param myContext * entity which this Details belongs to */ public void setContext(IObject myContext) { _context = myContext; } public abstract Details newInstance(); /** * simple view of the Details. Accesses only the ids of the contained * entities */ @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Details:{"); toString(sb); sb.append("}"); return sb.toString(); } public void toString(StringBuilder sb) { sb.append(";perm="); sb.append(_perms == null ? null : _perms.toString()); if (_externalInfo != null) { sb.append(";external=" + _externalInfo.getId()); } sb.append("user="); sb.append(_owner == null ? null : _owner.getId()); sb.append(";group="); sb.append(_group == null ? null : _group.getId()); sb.append(";create="); sb.append(_creation == null ? null : _creation.getId()); sb.append(";update="); sb.append(_update == null ? null : _update.getId()); } // Persistent Getters & Setters // =========================================================== /** * Permissions is a component embedded into the Details component. Similar * rules apply as to Details, but it is <em>not</em> suggested that users * attempt to directly query Permissions object as its internal state is not * public. */ @Transient public Permissions getPermissions() { return _perms; } public void setPermissions(Permissions perms) { _perms = perms; } @Transient public ExternalInfo getExternalInfo() { return _externalInfo; } public void setExternalInfo(ExternalInfo info) { _externalInfo = info; } // Mapping defined by subclassees @Transient public Experimenter getOwner() { return _owner; } public void setOwner(Experimenter exp) { _owner = exp; } // Mapping defined by subclasses @Transient public Event getCreationEvent() { return _creation; } public void setCreationEvent(Event e) { _creation = e; } // Mapping defined by subclasses @Transient public ExperimenterGroup getGroup() { return _group; } public void setGroup(ExperimenterGroup _group) { this._group = _group; } // Mapping defined by subclasses @Transient public Event getUpdateEvent() { return _update; } public void setUpdateEvent(Event e) { _update = e; } // Getters & Setters // =========================================================== public boolean acceptFilter(Filter filter) { setPermissions((Permissions) filter.filter(PERMISSIONS, getPermissions())); setExternalInfo((ExternalInfo) filter.filter(EXTERNALINFO, getExternalInfo())); setOwner((Experimenter) filter.filter(OWNER, getOwner())); setGroup((ExperimenterGroup) filter.filter(GROUP, getGroup())); setCreationEvent((Event) filter.filter(CREATIONEVENT, getCreationEvent())); setUpdateEvent((Event) filter.filter(UPDATEEVENT, getUpdateEvent())); return true; } public Object retrieve(String field) { if (field == null) { return null; } else if (field.equals(OWNER)) { return getOwner(); } else if (field.equals(GROUP)) { return getGroup(); } else if (field.equals(CREATIONEVENT)) { return getCreationEvent(); } else if (field.equals(PERMISSIONS)) { return getPermissions(); } else if (field.equals(EXTERNALINFO)) { return getExternalInfo(); } else if (field.equals(UPDATEEVENT)) { return getUpdateEvent(); } else { if (_dynamicFields != null) { return _dynamicFields.get(field); } return null; } } public void putAt(String field, Object value) { if (field == null) { return; } else if (field.equals(OWNER)) { setOwner((Experimenter) value); } else if (field.equals(GROUP)) { setGroup((ExperimenterGroup) value); } else if (field.equals(CREATIONEVENT)) { setCreationEvent((Event) value); } else if (field.equals(PERMISSIONS)) { setPermissions((Permissions) value); } else if (field.equals(EXTERNALINFO)) { setExternalInfo((ExternalInfo) value); } else if (field.equals(UPDATEEVENT)) { setUpdateEvent((Event) value); } else { if (_dynamicFields == null) { _dynamicFields = new HashMap(); } _dynamicFields.put(field, value); } } }