/*
* ome.tools.hibernate.HibernateUtils
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.tools.hibernate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.Hibernate;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;
import ome.conditions.InternalException;
import ome.model.IObject;
import ome.model.core.Image;
import ome.model.internal.Details;
import ome.model.meta.Experimenter;
import ome.tools.lsid.LsidUtils;
/**
* contains methods for reloading {@link IObject#unload() unloaded} entities and
* nulled collections as well as determining the index of certain properties in
* a dehydrated Hibernate array.
*
* @author Josh Moore, josh.moore at gmx.de
* @since 3.0-M3
* @see <a
* href="http://trac.openmicroscopy.org.uk/ome/wiki/ObjectModel">wiki:ObjectModel</a>
*/
public abstract class HibernateUtils {
private static Logger log = LoggerFactory.getLogger(HibernateUtils.class);
// using Image as an example. All details fields are named the same.
private static String DETAILS = LsidUtils.parseField(Image.DETAILS);
// ~ Static methods
// =========================================================================
// TODO isUnloaded/nullSafe*Id actually belong in ModelUtils.
public static boolean isUnloaded(Object original) {
if (original != null && original instanceof IObject
&& !((IObject) original).isLoaded()) {
return true;
}
return false;
}
/**
* returns the id of the {@link Experimenter owner} of this entity, or null
* if: (1) the object is null, (2) the {@link Details} is null, (3) the
* owner is null.
*
* @param iobject
* Can be null.
* @return the id or null.
*/
public static Long nullSafeOwnerId(IObject iobject) {
if (iobject == null) {
return null;
}
if (iobject.getDetails() == null) {
return null;
}
if (iobject.getDetails().getOwner() == null) {
return null;
}
return iobject.getDetails().getOwner().getId();
}
/**
* returns the id of the {@link ome.model.meta.ExperimenterGroup group} of this entity,
* or null if: (1) the object is null, (2) the {@link Details} is null, (3) the
* group is null.
*
* @param iobject
* Can be null.
* @return the id or null.
*/
public static Long nullSafeGroupId(IObject iobject) {
if (iobject == null) {
return null;
}
if (iobject.getDetails() == null) {
return null;
}
if (iobject.getDetails().getGroup() == null) {
return null;
}
return iobject.getDetails().getGroup().getId();
}
/**
* loads collections which have been filtered or nulled by the user
*/
public static void fixNulledOrFilteredCollections(IObject entity,
IObject target, EntityPersister persister, SessionImplementor source) {
Object[] currentState = persister.getPropertyValues(entity, source
.getEntityMode());
Object[] previousState = persister.getPropertyValues(target, source
.getEntityMode());
String[] propertyNames = persister.getPropertyNames();
Type[] types = persister.getPropertyTypes();
int detailsIndex = detailsIndex(propertyNames);
Details d = (Details) currentState[detailsIndex];
if (d != null) {
Set<String> s = d.filteredSet();
for (String string : s) {
string = LsidUtils.parseField(string);
int idx = index(string, propertyNames);
Object previous = previousState[idx];
if (!(previous instanceof Collection)) // implies
// not null
{
throw new InternalException(String.format(
"Invalid collection found for filtered "
+ "field %s in previous state for %s",
string, entity));
}
log("Copying filtered collection ", string);
Collection copy = copy(((Collection) previous));
persister.setPropertyValue(entity, idx, copy, source
.getEntityMode());
}
}
for (int i = 0; i < types.length; i++) {
Type t = types[i];
if (t.isCollectionType() && null == currentState[i]) {
Object previous = previousState[i];
if (previous == null) {
// ignore. If the system gave it to us, it can handle it.
} else if (previous instanceof Collection) {
if (!Hibernate.isInitialized(previous)) {
log("Skipping uninitialized collection: ", propertyNames[i]);
persister.setPropertyValue(entity, i, previous, source
.getEntityMode());
} else {
log("Copying nulled collection: ", propertyNames[i]);
Collection copy = copy(((Collection) previous));
persister.setPropertyValue(entity, i, copy, source
.getEntityMode());
}
} else if (previous instanceof Map) {
if (!Hibernate.isInitialized(previous)) {
log("Skipping uninitialized map: ", propertyNames[i]);
persister.setPropertyValue(entity, i, previous, source
.getEntityMode());
} else {
Map copy = copy((Map) previous);
persister.setPropertyValue(entity, i, copy, source
.getEntityMode());
}
} else {
throw new InternalException(String.format(
"Invalid collection found for null "
+ "field %s in previous state for %s",
propertyNames[i], entity));
}
}
}
}
/**
* @param new_d the new details
* @param old_d the old details
* @return if the non-permissions fields are the same
*/
public static boolean onlyPermissionsChanged(Details new_d, Details old_d) {
if (idEqual(new_d.getOwner(), old_d.getOwner())
&& idEqual(new_d.getGroup(), old_d.getGroup())
&& idEqual(new_d.getCreationEvent(), old_d.getCreationEvent())
&& idEqual(new_d.getUpdateEvent(), old_d.getUpdateEvent())
&& idEqual(new_d.getExternalInfo(), old_d.getExternalInfo())) {
return true;
}
return false;
}
/**
* returns true under the following circumstances:
* <ul>
* <li>both arguments are null, or</li>
* <li>both arguments are identical (==), or</li>
* <li>both arguments have the same id value(equals)</li>
* </ul>
*/
public static boolean idEqual(IObject arg1, IObject arg2) {
// arg1 is null
if (arg1 == null) {
// both are null, therefore equal
if (arg2 == null) {
return true;
}
// just arg1 is null, can't be equal
return false;
}
// just arg2 is null, also can't be equal
else if (arg2 == null) {
return false;
}
// neither argument is null,
// so let's move a level down,
// but first test reference equality
// as a performance op.
if (arg1 == arg2) {
return true; // OP
}
Long arg1_id = arg1.getId();
Long arg2_id = arg2.getId();
// arg1_id is null
if (arg1_id == null) {
// both are null, and not identical (see OP above)
// therefore different
if (arg2_id == null) {
return false;
}
// just arg2_id is null, can't be equal
return false;
}
// just arg2_id null, and also can't be equal
else if (arg2_id == null) {
return false;
} else {
return arg1_id.equals(arg2_id);
}
}
public static Details getDetails(Object[] state, String[] names) {
return (Details) state[detailsIndex(names)];
}
public static int detailsIndex(String[] propertyNames) {
return index(DETAILS, propertyNames);
}
public static int index(String str, String[] propertyNames) {
for (int i = 0; i < propertyNames.length; i++) {
if (propertyNames[i].equals(str)) {
return i;
}
}
throw new InternalException("No \"" + str + "\" property found.");
}
@SuppressWarnings("unchecked")
protected static Map copy(Map m) {
Map newMap = new HashMap();
newMap.putAll(m);
return newMap;
}
@SuppressWarnings("unchecked")
protected static Collection copy(Collection c) {
if (c instanceof Set) {
return new HashSet(c);
}
else if (c instanceof List) {
return new ArrayList(c);
} else {
throw new InternalException("Unsupported collection type:"
+ c.getClass().getName());
}
}
private static void log(Object... objects) {
if (log.isDebugEnabled() && objects != null && objects.length > 0) {
StringBuilder sb = new StringBuilder(objects.length * 16);
for (Object obj : objects) {
sb.append(obj.toString());
}
log.debug(sb.toString());
}
}
}