/* * $Id$ * * Copyright 2007-2014 Glencoe Software, Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt * */ package omero.util; import static omero.rtypes.rbool; import static omero.rtypes.rdouble; import static omero.rtypes.rfloat; import static omero.rtypes.rint; import static omero.rtypes.rinternal; import static omero.rtypes.rlist; import static omero.rtypes.rlong; import static omero.rtypes.rmap; import static omero.rtypes.robject; import static omero.rtypes.rstring; import static omero.rtypes.rtime; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import ome.conditions.InternalException; import ome.model.IObject; import ome.model.ModelBased; import ome.services.blitz.util.ConvertToBlitzExceptionMessage; import ome.system.OmeroContext; import ome.system.Principal; import ome.system.Roles; import ome.util.Filterable; import ome.util.ModelMapper; import ome.util.ReverseModelMapper; import ome.util.Utils; import omeis.providers.re.RGBBuffer; import omeis.providers.re.data.PlaneDef; import omeis.providers.re.data.RegionDef; import omero.ApiUsageException; import omero.RString; import omero.RTime; import omero.RType; import omero.ServerError; import omero.rtypes.Conversion; import omero.model.NamedValue; import omero.model.PermissionsI; import omero.romio.BlueBand; import omero.romio.GreenBand; import omero.romio.PlaneDefWithMasks; import omero.romio.RedBand; import omero.romio.XY; import omero.romio.XZ; import omero.romio.ZY; import omero.sys.EventContext; import omero.sys.Filter; import omero.sys.Options; import omero.sys.Parameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import Ice.UserException; /** * Responsible for the mapping of ome.* types to omero.* types and back again. * Not all types are bidirectional, rather only those mappings are needed that * actually appear in the blitz API. * * As of Beta3.1, an {@link IceMapper} instance can also be configured to handle * return value mapping, though by default an exception will be thrown if * {@link #mapReturnValue(Object)} is called. */ public class IceMapper extends ome.util.ModelMapper implements ReverseModelMapper { private static Logger log = LoggerFactory.getLogger(IceMapper.class); // Return value mapping // ========================================================================= private final ReturnMapping mapping; public IceMapper() { this.mapping = null; } public IceMapper(ReturnMapping mapping) { this.mapping = mapping; } public interface ReturnMapping { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException; } public final static ReturnMapping VOID = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws UserException { if (value != null) { throw new IllegalArgumentException("Method is void"); } return null; } }; public final static ReturnMapping FILTERABLE = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws UserException { return mapper.map((Filterable) value); } }; public final static ReturnMapping FILTERABLE_ARRAY = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws UserException { Filterable[] array = (Filterable[]) value; if (array == null) { return null; } else { List rv = new ArrayList(array.length); for (int i = 0; i < array.length; i++) { rv.add(mapper.map(array[i])); } return rv; } } }; public final static ReturnMapping FILTERABLE_COLLECTION = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws UserException { Collection<Filterable> coll = (Collection<Filterable>) value; if (coll == null) { return null; } else { List rv = new ArrayList(); for (Filterable f : coll) { rv.add(mapper.map(f)); } return rv; } } }; public final static ReturnMapping OBJECTARRAY_TO_RTYPESEQ = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } Object[] objArr = (Object[]) value; List<RType> rv = new ArrayList<RType>(); for (Object obj : objArr) { rv.add((RType) OBJECT_TO_RTYPE.mapReturnValue(mapper, obj)); } return rv; } }; @SuppressWarnings("unchecked") public final static ReturnMapping LISTOBJECTARRAY_TO_RTYPESEQSEQ = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } List<Object[]> listObjArr = (List<Object[]>) value; List<List<RType>> rv = new ArrayList<List<RType>>(); for (Object[] objs : listObjArr) { rv.add((List<RType>)OBJECTARRAY_TO_RTYPESEQ.mapReturnValue(mapper, objs)); } return rv; } }; public final static ReturnMapping OBJECT_TO_RTYPE = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { return mapper.toRType(value); } }; public final static ReturnMapping STRING_TO_RSTRING = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { String str = (String) value; return omero.rtypes.rstring(str); } }; /** * Specifies a return type which should not be parsed. This should * only be used for objects unknown to the Mapper, and should <em>not</em> * be used for any types which contain by transitivity any ome.model.* types! */ public final static ReturnMapping UNMAPPED = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { return value; } }; public final static ReturnMapping PRIMITIVE = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } else { if (!IceMapper.isNullablePrimitive(value.getClass())) { throw new RuntimeException( "Object not nullable primitive: " + value); } return value; } } }; public final static ReturnMapping PRIMITIVE_MAP = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } else { Map map = (Map) value; Map rv = new HashMap(); for (Object k : map.keySet()) { Object v = map.get(k); Object kr = PRIMITIVE.mapReturnValue(mapper, k); Object vr = PRIMITIVE.mapReturnValue(mapper, v); rv.put(kr, vr); } return rv; } } }; public final static ReturnMapping FILTERABLE_PRIMITIVE_MAP = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } else { Map map = (Map) value; Map rv = new HashMap(); for (Object k : map.keySet()) { Object v = map.get(k); Object kr = FILTERABLE.mapReturnValue(mapper, k); Object vr = PRIMITIVE.mapReturnValue(mapper, v); rv.put(kr, vr); } return rv; } } }; public final static ReturnMapping PRIMITIVE_FILTERABLE_COLLECTION_MAP = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } else { Map map = (Map) value; Map rv = new HashMap(); for (Object k : map.keySet()) { Object v = map.get(k); Object kr = PRIMITIVE.mapReturnValue(mapper, k); Object vr = FILTERABLE_COLLECTION.mapReturnValue(mapper, v); rv.put(kr, vr); } return rv; } } }; public final static ReturnMapping RTYPEDICT = new ReturnMapping() { public Object mapReturnValue(IceMapper mapper, Object value) throws Ice.UserException { if (value == null) { return null; } else { Map map = (Map) value; Map rv = new HashMap(); for (Object k : map.keySet()) { Object v = map.get(k); Object kr = PRIMITIVE.mapReturnValue(mapper, k); Object vr = OBJECT_TO_RTYPE.mapReturnValue(mapper, v); rv.put(kr, vr); } return rv; } } }; /** * Returns true only if the current mapping is the {@link #VOID} mapping. */ public boolean isVoid() { return canMapReturnValue() && mapping == VOID; } /** * True if this instance has a {@link ReturnMapping} */ public boolean canMapReturnValue() { return mapping != null; } /** * Convert the given Object via the set {@link ReturnMapping}. Throws a * {@link NullPointerException} if no mapping is set. */ public Object mapReturnValue(Object value) throws Ice.UserException { return mapping.mapReturnValue(this, value); } // Exception handling // ========================================================================= public static ServerError fillServerError(ServerError se, Throwable t) { se.message = t.getMessage(); se.serverExceptionClass = t.getClass().getName(); se.serverStackTrace = stackAsString(t); return se; } // Classes // ========================================================================= private static Class<? extends IObject> _class(String className) { Class k = null; try { k = Class.forName(className); } catch (Exception e) { // ok } return k; } public static Class<? extends IObject> omeroClass(String className, boolean strict) throws ApiUsageException { Class k = _class(className); // If that didn't work, try to prefix with "omero.model" if (k == null) { k = _class("omero.model." + className); } // If either of those attempts worked, map it with IceMap unless // it's already in the key of OMEtoOMERO if (k != null) { if (IceMap.OMEtoOMERO.containsKey(k)) { // good } else { k = IceMap.OMEROtoOME.get(k); } } // For whatever reason, it's not valid. Log it. if (k == null) { if (log.isDebugEnabled()) { log.debug(className + " does not specify a valid class."); } } if (k == null && strict) { ApiUsageException aue = new ApiUsageException(); aue.message = className + " does not specify a valid class."; throw aue; } // Return, even null. return k; } // Conversions // ========================================================================= public RType toRType(Object o) throws omero.ApiUsageException { if (o == null) { return null; } else if (o instanceof RType) { return (RType) o; } else if (o instanceof Boolean) { Boolean b = (Boolean) o; omero.RBool bool = rbool(b.booleanValue()); return bool; } else if (o instanceof Date) { Date date = (Date) o; omero.RTime time = rtime(date.getTime()); return time; } else if (o instanceof Integer) { Integer i = (Integer) o; omero.RInt rint = rint(i); return rint; } else if (o instanceof Long) { Long lng = (Long) o; omero.RLong rlng = rlong(lng.longValue()); return rlng; } else if (o instanceof Float) { Float flt = (Float) o; omero.RFloat rflt = rfloat(flt); return rflt; } else if (o instanceof Double) { Double dbl = (Double) o; omero.RDouble rdbl = rdouble(dbl.doubleValue()); return rdbl; } else if (o instanceof String) { String str = (String) o; omero.RString rstr = rstring(str); return rstr; } else if (o instanceof ome.util.PermDetails) { // Implements IObject as well! ome.util.PermDetails p = (ome.util.PermDetails) o; return toRType(p.getDetails().getPermissions()); } else if (o instanceof IObject) { IObject obj = (IObject) o; omero.model.IObject om = (omero.model.IObject) map(obj); omero.RObject robj = robject(om); return robj; } else if (o instanceof Collection) { List<RType> l = new ArrayList<RType>(); for (Object i : (Collection) o) { l.add(toRType(i)); } return rlist(l); } else if (o instanceof Map) { Map<?, ?> mIn = (Map) o; Map<String, RType> mOut = new HashMap<String, RType>(); for (Object k : mIn.keySet()) { if (!(k instanceof String)) { throw new omero.ValidationException( null, null, "Map key not a string"); } mOut.put((String) k, toRType(mIn.get(k))); } return rmap(mOut); } else if (o instanceof omero.Internal) { return rinternal((omero.Internal) o); } else if (o instanceof ome.model.internal.Permissions) { ome.model.internal.Permissions p = (ome.model.internal.Permissions) o; Map<String, RType> rv = new HashMap<String, RType>(); rv.put("perm", rstring(p.toString())); rv.put("canAnnotate", rbool(!p.isDisallowAnnotate())); rv.put("canDelete", rbool(!p.isDisallowDelete())); rv.put("canEdit", rbool(!p.isDisallowEdit())); rv.put("canLink", rbool(!p.isDisallowLink())); return rmap(rv); } else if (o instanceof ome.model.units.Unit) { ome.model.units.Unit u = (ome.model.units.Unit) o; Map<String, RType> rv = new HashMap<String, RType>(); rv.put("value", rdouble(u.getValue())); rv.put("unit", rstring(u.getUnit().toString())); rv.put("symbol", rstring(u.getUnit().getSymbol())); return rmap(rv); } else if (o instanceof ome.model.units.UnitEnum) { return rstring(((ome.model.units.UnitEnum) o).toString()); } else { throw new ApiUsageException(null, null, "Unsupported conversion to rtype from " + o.getClass().getName() + ":" + o); } } /** * Uses the omero.rtypes hierarchy to properly convert any {@link RType} to * its internal representation. This requires that the instance properly * implement {@link omero.rtypes.Conversion} otherwise ApiUsageException * will be thrown. * * @param rt the wrapped value * @return the value unwrapped * @throws omero.ApiUsageException if the given value is not wrapped */ public Object fromRType(RType rt) throws omero.ApiUsageException { if (rt == null) { return null; } if (rt instanceof omero.rtypes.Conversion) { omero.rtypes.Conversion conv = (omero.rtypes.Conversion) rt; return conv.convert(this); } else { omero.ApiUsageException aue = new omero.ApiUsageException(); aue.message = rt.getClass() + " is not a conversion type"; throw aue; } } public static EventContext convert(ome.system.EventContext ctx) { if (ctx == null) { return null; } EventContext ec = new EventContext(); Long event = ctx.getCurrentEventId(); ec.eventId = event == null ? -1 : event; Long shareId = ctx.getCurrentShareId(); ec.shareId = shareId == null ? -1 : shareId; ec.sessionId = ctx.getCurrentSessionId(); ec.sessionUuid = ctx.getCurrentSessionUuid(); ec.eventType = ctx.getCurrentEventType(); ec.groupId = ctx.getCurrentGroupId(); ec.groupName = ctx.getCurrentGroupName(); ec.userId = ctx.getCurrentUserId(); ec.userName = ctx.getCurrentUserName(); ec.leaderOfGroups = ctx.getLeaderOfGroupsList(); ec.memberOfGroups = ctx.getMemberOfGroupsList(); ec.isAdmin = ctx.isCurrentUserAdmin(); // ticket:2265 Removing from public interface // ec.isReadOnly = ctx.isReadOnly(); ec.groupPermissions = convert(ctx.getCurrentGroupPermissions()); return ec; } public static omero.romio.RGBBuffer convert(RGBBuffer buffer) { omero.romio.RGBBuffer b = new omero.romio.RGBBuffer(); b.bands = new byte[3][]; b.bands[RedBand.value] = buffer.getRedBand(); b.bands[GreenBand.value] = buffer.getGreenBand(); b.bands[BlueBand.value] = buffer.getBlueBand(); b.sizeX1 = buffer.getSizeX1(); b.sizeX2 = buffer.getSizeX2(); return b; } /** * Converts the passed Ice Object and returns the converted object. * * @param def The object to convert * @return See above. * @throws omero.ApiUsageException Thrown if the slice is unknown. */ public static PlaneDef convert(omero.romio.PlaneDef def) throws omero.ApiUsageException { PlaneDef pd = new PlaneDef(def.slice, def.t); pd.setStride(def.stride); omero.romio.RegionDef r = def.region; if (r != null) { pd.setRegion(new RegionDef(r.x, r.y, r.width, r.height)); } if (def instanceof PlaneDefWithMasks) { pd.setRenderShapes(true); if (((PlaneDefWithMasks) def).shapeIds != null) { pd.setShapeIds(((PlaneDefWithMasks) def).shapeIds); } } switch (def.slice) { case XY.value: pd.setZ(def.z); break; case ZY.value: pd.setX(def.x); break; case XZ.value: pd.setY(def.y); break; default: omero.ApiUsageException aue = new omero.ApiUsageException(); aue.message = "Unknown slice for " + def; throw aue; } return pd; } public static Principal convert(omero.sys.Principal old) { if (old == null) { return null; } return new Principal(old.name, old.group, old.eventType); } public static omero.sys.Roles convert(Roles roles) { omero.sys.Roles r = new omero.sys.Roles(); r.rootId = roles.getRootId(); r.rootName = roles.getRootName(); r.systemGroupId = roles.getSystemGroupId(); r.systemGroupName = roles.getSystemGroupName(); r.userGroupId = roles.getUserGroupId(); r.userGroupName = roles.getUserGroupName(); r.guestId = roles.getGuestId(); r.guestName = roles.getGuestName(); r.guestGroupId = roles.getGuestGroupId(); r.guestGroupName = roles.getGuestGroupName(); return r; } public static RTime convert(Date date) { return rtime(date); } public static Timestamp convert(RTime time) { if (time == null) { return null; } return new Timestamp(time.getValue()); } public ome.parameters.Parameters convert(Parameters params) throws ApiUsageException { if (params == null) { return null; } ome.parameters.Parameters p = new ome.parameters.Parameters(); if (params.map != null) { for (String name : params.map.keySet()) { Object obj = params.map.get(name); p.add(convert(name, obj)); } } if (params.theFilter != null) { p.setFilter(convert(params.theFilter)); } if (params.theOptions != null) { p.setOptions(convert(params.theOptions)); } return p; } public ome.parameters.QueryParameter convert(String key, Object o) throws ApiUsageException { if (o == null) { return null; } String name = key; Class klass = o.getClass(); Object value = null; if (RType.class.isAssignableFrom(klass)) { value = fromRType((RType) o); // If fromRType passes correctly, then we're sure that we // can convert to rtypes.Conversion klass = ((Conversion) o).type(); } else { omero.ApiUsageException aue = new omero.ApiUsageException(); aue.message = "Query parameter must be a subclass of RType " + o; throw aue; } ome.parameters.QueryParameter qp = new ome.parameters.QueryParameter( name, klass, value); return qp; } public static ome.parameters.Options convert(Options o) { if (o == null) { return null; } ome.parameters.Options options = new ome.parameters.Options(); if (o.orphan != null) { options.orphan = o.orphan.getValue(); } if (o.leaves != null) { options.leaves= o.leaves.getValue(); } if (o.acquisitionData != null) { options.acquisitionData = o.acquisitionData.getValue(); } return options; } public static ome.parameters.Filter convert(Filter f) { if (f == null) { return null; } ome.parameters.Filter filter = new ome.parameters.Filter(); if (f.offset != null) { filter.offset = f.offset.getValue(); } if (f.limit != null) { filter.limit = f.limit.getValue(); } if (f.ownerId != null) { filter.owner(f.ownerId.getValue()); } if (f.groupId != null) { filter.group(f.groupId.getValue()); } if (f.startTime != null) { filter.startTime = convert(f.startTime); } if (f.endTime != null) { filter.endTime = convert(f.endTime); } if (f.unique != null && f.unique.getValue()) { filter.unique(); } return filter; } public static List<NamedValue> convertNamedValueList(List<ome.model.internal.NamedValue> map) { if (map == null) { return null; } final List<NamedValue> nvl = new ArrayList<NamedValue>(map.size()); for (final ome.model.internal.NamedValue nv : map) { if (nv == null) { nvl.add(null); } else { final String name = nv.getName(); final String value = nv.getValue(); nvl.add(new NamedValue(name, value)); } } return nvl; } public static List<NamedValue> convertMapPairs(List<ome.xml.model.MapPair> map) { if (map == null) { return null; } final List<NamedValue> nvl = new ArrayList<NamedValue>(map.size()); for (final ome.xml.model.MapPair nv : map) { if (nv == null) { nvl.add(null); } else { final String name = nv.getName(); final String value = nv.getValue(); nvl.add(new NamedValue(name, value)); } } return nvl; } /** * Convert a String→String map's values to {@link RString}s. * <code>null</code> values are dropped completely. * @param map a map * @return the converted map, or <code>null</code> if <code>map == null</code> */ public static Map<String, RString> convertStringStringMap(Map<String, String> map) { if (map == null) { return null; } final Map<String, RString> rMap = new HashMap<String, RString>(map.size()); for (final Map.Entry<String, String> mapEntry : map.entrySet()) { final String key = mapEntry.getKey(); final String value = mapEntry.getValue(); if (value != null) { rMap.put(key, rstring(value)); } } return rMap; } /** * Overrides the findCollection logic of {@link ModelMapper}, since all * {@link Collection}s should be {@link List}s in Ice. */ @Override public Collection findCollection(Collection source) { if (source == null) { return null; } Collection target = (Collection) model2target.get(source); if (null == target) { target = new ArrayList(); model2target.put(source, target); } return target; } public List map(Filterable[] array) { if (array == null) { return null; } else if (array.length == 0) { return new ArrayList(); } else { List l = new ArrayList(array.length); for (int i = 0; i < array.length; i++) { l.add(map(array[i])); } return l; } } // ~ For Reversing (omero->ome). Copied from ReverseModelMapper. // ========================================================================= protected Map<Object, Object> target2model = new IdentityHashMap<Object, Object>(); public static omero.model.Permissions convert(ome.model.internal.Permissions p) { if (p == null) { return null; } return new PermissionsI(p.toString()); } public static ome.model.internal.Permissions convert(omero.model.Permissions p) { if (p == null) { return null; } return Utils.toPermissions(p.getPerm1()); } // TODO copied with ModelMapper public boolean isImmutable(Object obj) { if (null == obj || obj instanceof Number || obj instanceof Number[] || obj instanceof String || obj instanceof String[] || obj instanceof Boolean || obj instanceof Boolean[]) { return true; } return false; } public Object reverse(Object source) throws ApiUsageException { if (source == null) { return null; } else if (Collection.class.isAssignableFrom(source.getClass())) { return reverse((Collection) source); } else if (ModelBased.class.isAssignableFrom(source.getClass())) { return reverse((ModelBased) source); } else if (isImmutable(source)) { return source; } else if (RType.class.isAssignableFrom(source.getClass())) { return fromRType((RType) source); } else { omero.ApiUsageException aue = new omero.ApiUsageException(); aue.message = "Don't know how to reverse " + source; throw aue; } } /** * Copied from {@link ModelMapper#findCollection(Collection)} This could be * unified in that a method findCollection(Collection, Map) was added with * {@link ModelMapper} calling {@code findCollection(source, model2target)} * and {@code reverseCollection(Collection)} calling * {@code findCollection(source,target2model)}. * * @param source the wrapped {@link Collection} * @return the {@link Collection} unwrapped */ public Collection reverse(Collection source) { // FIXME throws // omero.ApiUsageException { return reverse(source, source == null ? null : source.getClass()); } /** * Creates a collection assignable to the given type. Currently only * {@link Set} and {@link List} are supported, and {@link HashSet}s and * {@link ArrayList}s will be returned. The need for this arose from the * decision to have no {@link Set}s in the Ice Java mapping. * * @see <a href="http://trac.openmicroscopy.org/ome/ticket/684">Trac ticket #684</a> */ public Collection reverse(Collection source, Class targetType) { // FIXME // throws // omero.ApiUsageException // { if (source == null) { return null; } Collection target = (Collection) target2model.get(source); if (null == target) { if (Set.class.isAssignableFrom(targetType)) { target = new HashSet(); } else if (List.class.isAssignableFrom(targetType)) { target = new ArrayList(); } else { // omero.ApiUsageException aue = new omero.ApiUsageException(); // aue.message = "Unknown collection type "+targetType; // throw aue; throw new InternalException("Unknown collection type " + targetType); } target2model.put(source, target); try { for (Object object : source) { target.add(reverse(object)); } } catch (ApiUsageException aue) { // FIXME reverse can't throw // ServerErrors! convertAndThrow(aue); } } return target; } /** * Supports the separate case of reversing for arrays. See * {@link #reverse(Collection, Class)} and {@link #map(Filterable[])}. */ public Object[] reverseArray(List list, Class type) throws omero.ServerError { if (list == null) { return null; } Class component = type.getComponentType(); Object[] array = null; try { array = (Object[]) Array.newInstance(component, list.size()); for (int i = 0; i < array.length; i++) { array[i] = this.handleInput(component, list.get(i)); } } catch (Exception e) { String msg = "Cannot create array from type " + type; if (log.isErrorEnabled()) { log.error(msg, e); } omero.ApiUsageException aue = new omero.ApiUsageException(); aue.message = msg; aue.serverExceptionClass = e.getClass().getName(); throw aue; } return array; } public Map reverse(Map map) { if (map == null) { return null; } if (target2model.containsKey(map)) { return (Map) target2model.get(map); } Map<Object, Object> target = new HashMap<Object, Object>(); target2model.put(map, target); try { for (Object key : map.keySet()) { Object value = map.get(key); Object targetKey = reverse(key); Object targetValue = reverse(value); target.put(targetKey, targetValue); } } catch (ApiUsageException aue) { convertAndThrow(aue); } return target; } /** * Copied from {@link ReverseModelMapper}. */ public Filterable reverse(ModelBased source) { if (source == null) { return null; } else if (target2model.containsKey(source)) { return (Filterable) target2model.get(source); } else { Filterable object = source.fillObject(this); target2model.put(source, object); return object; } } /** * Reverse a String→String map's values from {@link RString}s. * <code>null</code> values are dropped completely. * @param rMap a map * @return the reversed map, or <code>null</code> if <code>rMap == null</code> */ public static Map<String, String> reverseStringStringMap(Map<String, RString> rMap) { if (rMap == null) { return null; } final Map<String, String> map = new HashMap<String, String>(rMap.size()); for (final Map.Entry<String, RString> rMapEntry : rMap.entrySet()) { final String key = rMapEntry.getKey(); final RString rValue = rMapEntry.getValue(); final String value = rValue == null ? null : rValue.getValue(); if (value != null) { map.put(key, value); } } return map; } public static List<ome.model.internal.NamedValue> reverseNamedList(List<NamedValue> map) { if (map == null) { return null; } final List<ome.model.internal.NamedValue> nvl = new ArrayList<ome.model.internal.NamedValue>(map.size()); for (final NamedValue nv : map) { if (nv == null) { nvl.add(null); } else { final String name = nv.name; final String value = nv.value; nvl.add(new ome.model.internal.NamedValue(name, value)); } } return nvl; } public void store(Object source, Object target) { target2model.put(source, target); } // ~ For ome->omero parsing // ========================================================================= @Override protected Map c2c() { return IceMap.OMEtoOMERO; } private void fillTarget(Filterable source, ModelBased target) { if (source != null && target != null) { target.copyObject(source, this); } } @Override public Filterable filter(String fieldId, Filterable source) { // Filterable o = super.filter(fieldId,source); // Can't call super here!! if (hasntSeen(source)) { // log.info("Haven't seen. Stepping into "+f); enter(source); addSeen(source); source.acceptFilter(this); exit(source); } Object target = findTarget(source); fillTarget(source, (ModelBased) target); // FIXME cast return source; } @Override protected boolean hasntSeen(Object o) { return o == null ? false : super.hasntSeen(o); } private void convertAndThrow(ApiUsageException aue) { InternalException ie = new InternalException(aue.getMessage()); ie.setStackTrace(aue.getStackTrace()); } // ~ Methods from IceMethodInvoker // ========================================================================= protected boolean isPrimitive(Class<?> p) { if (p.equals(byte.class) || p.equals(byte[].class) || p.equals(int.class) || p.equals(int[].class) || p.equals(long.class) || p.equals(long[].class) || p.equals(double.class) || p.equals(double[].class) || p.equals(float.class) || p.equals(float[].class) || p.equals(boolean.class) || p.equals(boolean[].class) || p.equals(String.class)) { return true; } return false; } protected static boolean isNullablePrimitive(Class<?> p) { if (p.equals(String.class) || p.equals(Integer.class) || p.equals(Integer[].class) || p.equals(Long.class) || p.equals(Long[].class) || p.equals(Float.class) || p.equals(Float[].class) || p.equals(Double.class) || p.equals(Double[].class) || p.equals(Boolean.class) || p.equals(Boolean[].class)) { return true; } return false; } protected static boolean isWrapperArray(Class<?> p) { if (p.equals(Integer[].class) || p.equals(Long[].class) || p.equals(Double[].class) || p.equals(Float[].class) || p.equals(String[].class)) { return true; } return false; } public Object handleInput(Class<?> p, Object arg) throws ServerError { if (arg instanceof RType) { RType rt = (RType) arg; return fromRType(rt); } else if (isPrimitive(p) || isNullablePrimitive(p)) { // FIXME use findTarget for Immutable. return arg; } else if (isWrapperArray(p)) { return reverseArray((List) arg, p); } else if (p.equals(Class.class)) { return omeroClass((String) arg, true); } else if (ome.model.internal.Details.class.isAssignableFrom(p)) { return reverse((ModelBased) arg); } else if (ome.model.IObject.class.isAssignableFrom(p)) { return reverse((ModelBased) arg); } else if (p.equals(ome.parameters.Filter.class)) { return convert((omero.sys.Filter) arg); } else if (p.equals(ome.system.Principal.class)) { return convert((omero.sys.Principal) arg); } else if (p.equals(ome.parameters.Parameters.class)) { return convert((omero.sys.Parameters) arg); } else if (List.class.isAssignableFrom(p)) { return reverse((Collection) arg); } else if (Set.class.isAssignableFrom(p)) { return reverse(new HashSet((Collection) arg)); // Necessary // since Ice // doesn't // support // Sets. } else if (Collection.class.isAssignableFrom(p)) { return reverse((Collection) arg); } else if (Timestamp.class.isAssignableFrom(p)) { if (arg != null) { throw new RuntimeException("This must be null here"); } return null; } else if (Map.class.isAssignableFrom(p)) { return reverse((Map) arg); } else if (PlaneDef.class.isAssignableFrom(p)) { return convert((omero.romio.PlaneDef) arg); } else if (Object[].class.isAssignableFrom(p)) { return reverseArray((List) arg, p); } else { throw new ApiUsageException(null, null, "Can't handle input " + p); } } public Object handleOutput(Class type, Object o) throws ServerError { if (o == null) { return null; } else if (RType.class.isAssignableFrom(type)) { return o; } else if (omero.Internal.class.isAssignableFrom(type)) { return rinternal((omero.Internal) o); } else if (void.class.isAssignableFrom(type)) { assert o == null; return null; } else if (isPrimitive(type)) { return o; } else if (isNullablePrimitive(type)) { return toRType(o); } else if (RGBBuffer.class.isAssignableFrom(type)) { return convert((RGBBuffer) o); } else if (Roles.class.isAssignableFrom(type)) { return convert((Roles) o); } else if (Date.class.isAssignableFrom(type)) { return convert((Date) o); } else if (ome.system.EventContext.class.isAssignableFrom(type)) { return convert((ome.system.EventContext) o); } else if (Set.class.isAssignableFrom(type)) { return map(new ArrayList((Set) o)); // Necessary since Ice // doesn't support Sets. } else if (Collection.class.isAssignableFrom(type)) { return map((Collection) o); } else if (IObject.class.isAssignableFrom(type)) { return map((Filterable) o); } else if (Map.class.isAssignableFrom(type)) { return map((Map) o); } else if (Filterable[].class.isAssignableFrom(type)) { return map((Filterable[]) o); } else { throw new ApiUsageException(null, null, "Can't handle output " + type); } } /** * wraps any non-ServerError returned by * {@link #handleException(Throwable, OmeroContext)} in an * {@link InternalException}. */ public ServerError handleServerError(Throwable t, OmeroContext ctx) { Ice.UserException ue = handleException(t, ctx); if (ue instanceof ServerError) { return (ServerError) ue; } omero.InternalException ie = new omero.InternalException(); IceMapper.fillServerError(ie, ue); return ie; } public Ice.UserException handleException(Throwable t, OmeroContext ctx) { // Getting rid of the reflection wrapper. if (InvocationTargetException.class.isAssignableFrom(t.getClass())) { t = t.getCause(); } if (log.isDebugEnabled()) { log.debug("Handling:", t); } // First we give registered handlers a chance to convert the message, // if that doesn't succeed, then we try either manually, or just // wrap the exception in an InternalException if (ctx != null) { try { ConvertToBlitzExceptionMessage ctbem = new ConvertToBlitzExceptionMessage(this, t); ctx.publishMessage(ctbem); if (ctbem.to != null) { t = ctbem.to; } } catch (Throwable handlerT) { // Logging the output, but we shouldn't worry the user // with a failing handler log.error("Exception handler failure", handlerT); } } Class c = t.getClass(); if (Ice.UserException.class.isAssignableFrom(c)) { return (Ice.UserException) t; } // API USAGE else if (ome.conditions.OptimisticLockException.class .isAssignableFrom(c)) { omero.OptimisticLockException ole = new omero.OptimisticLockException(); return IceMapper.fillServerError(ole, t); } else if (ome.conditions.OverUsageException.class.isAssignableFrom(c)) { omero.OverUsageException oue = new omero.OverUsageException(); return IceMapper.fillServerError(oue, t); } else if (ome.services.query.QueryException.class.isAssignableFrom(c)) { omero.QueryException qe = new omero.QueryException(); return IceMapper.fillServerError(qe, t); } else if (ome.conditions.ValidationException.class.isAssignableFrom(c)) { omero.ValidationException ve = new omero.ValidationException(); return IceMapper.fillServerError(ve, t); } else if (ome.conditions.ApiUsageException.class.isAssignableFrom(c)) { omero.ApiUsageException aue = new omero.ApiUsageException(); return IceMapper.fillServerError(aue, t); } // CONCURRENCY else if (ome.conditions.MissingPyramidException.class .isAssignableFrom(c)) { omero.MissingPyramidException mpe = new omero.MissingPyramidException(); mpe.backOff = ((ome.conditions.MissingPyramidException) t).backOff; mpe.pixelsID = ((ome.conditions.MissingPyramidException) t).getPixelsId(); return IceMapper.fillServerError(mpe, t); } else if (ome.conditions.TryAgain.class .isAssignableFrom(c)) { omero.TryAgain ta = new omero.TryAgain(); ta.backOff = ((ome.conditions.TryAgain) t).backOff; return IceMapper.fillServerError(ta, t); } else if (ome.conditions.LockTimeout.class .isAssignableFrom(c)) { omero.LockTimeout lt = new omero.LockTimeout(); lt.backOff = ((ome.conditions.LockTimeout) t).backOff; lt.seconds = ((ome.conditions.LockTimeout) t).seconds; return IceMapper.fillServerError(lt, t); } else if (ome.conditions.DatabaseBusyException.class.isAssignableFrom(c)) { omero.DatabaseBusyException dbe = new omero.DatabaseBusyException(); return IceMapper.fillServerError(dbe, t); } else if (ome.conditions.ConcurrencyException.class.isAssignableFrom(c)) { omero.ConcurrencyException re = new omero.ConcurrencyException(); return IceMapper.fillServerError(re, t); } // RESOURCE else if (ome.conditions.ResourceError.class.isAssignableFrom(c)) { omero.ResourceError re = new omero.ResourceError(); return IceMapper.fillServerError(re, t); } // SECURITY else if (ome.conditions.ReadOnlyGroupSecurityViolation.class.isAssignableFrom(c)) { omero.ReadOnlyGroupSecurityViolation sv = new omero.ReadOnlyGroupSecurityViolation(); return IceMapper.fillServerError(sv, t); } else if (ome.conditions.GroupSecurityViolation.class.isAssignableFrom(c)) { omero.GroupSecurityViolation sv = new omero.GroupSecurityViolation(); return IceMapper.fillServerError(sv, t); } else if (ome.conditions.SecurityViolation.class.isAssignableFrom(c)) { omero.SecurityViolation sv = new omero.SecurityViolation(); return IceMapper.fillServerError(sv, t); } // SESSIONS else if (ome.conditions.RemovedSessionException.class .isAssignableFrom(c)) { omero.RemovedSessionException rse = new omero.RemovedSessionException(); return IceMapper.fillServerError(rse, t); } else if (ome.conditions.SessionTimeoutException.class .isAssignableFrom(c)) { omero.SessionTimeoutException ste = new omero.SessionTimeoutException(); return IceMapper.fillServerError(ste, t); } else if (ome.conditions.AuthenticationException.class .isAssignableFrom(c)) { // not an omero.ServerError() omero.AuthenticationException ae = new omero.AuthenticationException( t.getMessage()); return ae; } else if (ome.conditions.ExpiredCredentialException.class .isAssignableFrom(c)) { // not an omero.ServerError() omero.ExpiredCredentialException ece = new omero.ExpiredCredentialException( t.getMessage()); return ece; } // INTERNAL etc. else if (ome.conditions.InternalException.class.isAssignableFrom(c)) { omero.InternalException ie = new omero.InternalException(); return IceMapper.fillServerError(ie, t); } else if (ome.conditions.RootException.class.isAssignableFrom(c)) { // Not returning but logging error message. log .error("RootException thrown which is an unknown subclasss.\n" + "This most likely means that an exception was added to the\n" + "ome.conditions hierarchy, without being accountd for in blitz:\n" + c.getName()); } // Catch all in case above did not return omero.InternalException ie = new omero.InternalException(); return IceMapper.fillServerError(ie, t); } }