/*
* This file is part of ADDIS (Aggregate Data Drug Information System).
* ADDIS is distributed from http://drugis.org/.
* Copyright © 2009 Gert van Valkenhoef, Tommi Tervonen.
* Copyright © 2010 Gert van Valkenhoef, Tommi Tervonen, Tijs Zwinkels,
* Maarten Jacobs, Hanno Koeslag, Florin Schimbinschi, Ahmad Kamal, Daniel
* Reid.
* Copyright © 2011 Gert van Valkenhoef, Ahmad Kamal, Daniel Reid, Florin
* Schimbinschi.
* Copyright © 2012 Gert van Valkenhoef, Daniel Reid, Joël Kuiper, Wouter
* Reckman.
* Copyright © 2013 Gert van Valkenhoef, Joël Kuiper.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.drugis.addis.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import org.drugis.addis.entities.Entity;
import org.drugis.addis.entities.TypeWithName;
import org.drugis.common.EqualsUtil;
public class EntityUtil {
public static boolean deepEqual(Entity o1, Entity o2) {
return o1 == null ? o2 == null : o1.deepEquals(o2);
}
public static boolean deepEqual(Collection<? extends Entity> o1, Collection<? extends Entity> o2) {
return deepEqual(new ArrayList<Entity>(o1), new ArrayList<Entity>(o2));
}
public static boolean deepEqual(List<? extends Entity> o1, List<? extends Entity> o2) {
if (o1.size() == o2.size()) {
for (int i = 0; i < o1.size(); ++i) {
if (!EntityUtil.deepEqual(o1.get(i), o2.get(i))) {
return false;
}
}
return true;
}
return false;
}
/**
* Assumes that the key in <key, value> pairs is an Object to which a regular equals() is applicable.
*/
public static boolean deepEqual(Map<?, ? extends Entity> o1, Map<?, ? extends Entity> o2) {
if (o1.keySet().size() != o2.keySet().size()) return false;
for (Entry<?,? extends Entity> entry : o1.entrySet()) {
Object actualKey = findMatchingKey(entry.getKey(), o2.keySet());
if (actualKey == null) return false;
if (!deepEqual(entry.getValue(), o2.get(actualKey)))
return false;
}
return true;
}
/**
* Find a key in a set using Object.equals or by using Object.shallowEquals(Object o) using reflection
* Object.shallowEquals may be implemented to use a different equals criteria and is invoked after Object.equals
* @param key the key to find
* @param keySet the set to find key
* @return null or the element in the keySet
*/
private static Object findMatchingKey(Object key, Set<?> keySet) {
for (Object otherKey : keySet) {
if (EqualsUtil.equal(key, otherKey)) {
return otherKey;
}
java.lang.reflect.Method shallowEquals;
try {
shallowEquals = key.getClass().getMethod("deepEquals", Object.class);
boolean shallowEqual = ((Boolean)shallowEquals.invoke(key, otherKey));
if (shallowEqual) return otherKey;
} catch (NoSuchMethodException e) {
continue;
} catch (Exception e) {
throw new RuntimeException("Reflection failed for shallowEquals on: " + e);
}
}
return null;
}
/**
* Flattens a Collection of Entities by dynamically calling getContents() on its members
* @return a Collection of type T containing all the elements resulting from getContents()
*/
@SuppressWarnings("unchecked")
public static <T extends Entity> Collection<T> flatten(Collection<? extends Entity> set) {
HashSet<T> flat = new HashSet<T>();
for (Entity nested : set) {
java.lang.reflect.Method getContents;
try {
getContents = nested.getClass().getMethod("getContents");
flat.addAll((Collection<T>)getContents.invoke(nested));
} catch (NoSuchMethodException e) {
continue;
} catch (Exception e) {
throw new RuntimeException("Reflection failed for getContents on: " + e);
}
}
return flat;
}
/**
* Add a collection of entities and their dependencies to the set of dependencies.
* @param dependencies Dependencies to add to.
* @param entities Entities to add recursively.
*/
public static void addRecursiveDependencies(Set<Entity> dependencies, Collection<? extends Entity> entities) {
dependencies.addAll(entities);
for (Entity e : entities) {
dependencies.addAll(e.getDependencies());
}
}
public static Duration createDuration(String durationStr) {
try {
return DatatypeFactory.newInstance().newDuration(durationStr);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Return a concrete super-type of the given type, or Entity.class.
* If the given class is not an interface, return the given class itself.
* If the given type is an interface, return Entity.class if the interface is a sub-type of Entity, and Object.class otherwise.
* @param type The class to convert.
* @return The concrete type.
*/
public static Class<?> getConcreteTypeOrEntity(Class<?> type) {
if (type.isPrimitive()) {
if (type == boolean.class) {
return Boolean.class;
}
if (type == byte.class) {
return Byte.class;
}
if (type == char.class) {
return Character.class;
}
if (type == short.class) {
return Short.class;
}
if (type == int.class) {
return Integer.class;
}
if (type == long.class) {
return Long.class;
}
if (type == float.class) {
return Float.class;
}
if (type == double.class) {
return Double.class;
}
}
if (type.isInterface()) {
return Entity.class.isAssignableFrom(type) ? Entity.class : Object.class;
}
return type;
}
public static <T extends TypeWithName> T findByName(Collection<T> haystack, String needle) {
for (T o : haystack) {
if (needle.equals(o.getName())) {
return o;
}
}
return null;
}
}