/*
* ome.util.ModelMapper
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
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.model.meta.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Josh Moore <a
* href="mailto:josh.moore@gmx.de">josh.moore@gmx.de</a>
* @version 1.0
* @since 1.0
*/
public abstract class ModelMapper extends ContextFilter {
protected final static Logger log = LoggerFactory.getLogger(ModelMapper.class);
/**
* TODO identity versus null mappings
*
* @return a map from {@link IObject} classes {@link ModelBased} classes.
*/
protected abstract Map c2c();
protected Map model2target = new IdentityHashMap();
public ModelBased map(Filterable source) {
Filterable o = this.filter("MAPPING...", source);
return (ModelBased) model2target.get(o);
}
public Collection map(Collection source) {
Collection o = this.filter("MAPPING...", source);
return (Collection) model2target.get(o);
}
public Map map(Map source) {
Map o = this.filter("MAPPING...", source);
return (Map) model2target.get(o);
}
@Override
public Filterable filter(String fieldId, Filterable source) {
Filterable o = super.filter(fieldId, source);
ModelBased target = (ModelBased) findTarget(o);
fillTarget(source, target);
return o;
}
@Override
public Collection filter(String fieldId, Collection source) {
Collection o = super.filter(fieldId, source);
Collection target = findCollection(o);
fillCollection(source, target);
return o;
}
@Override
public Map filter(String fieldId, Map source) {
Map o = super.filter(fieldId, source);
Map target = findMap(o);
fillMap(source, target);
return o;
}
protected Class findClass(Class source) {
return (Class) c2c().get(Utils.trueClass(source));
}
/**
* extension point which subclasses can override to better map the
* keys of maps.
*/
public Object findKeyTarget(Object current) {
return findTarget(current);
}
/**
* extension point which subclasses can override to better map the
* values of collections and maps.
*/
public Object findCollectionTarget(Object current) {
return findTarget(current);
}
/**
* known immutables are return unchanged.
*
* @param current
* @return a possibly uninitialized object which will be finalized as the
* object graph is walked.
*/
public Object findTarget(Object current) {
// IMMUTABLES
if (null == current || current instanceof Number
|| current instanceof String || current instanceof Boolean
|| current instanceof Timestamp || current instanceof Class) {
return current;
}
Object target = model2target.get(current);
if (null == target) {
Class currentType = current.getClass();
Class targetType = null;
if (currentType.isArray()) {
Class componentType = null;
try {
int length = Array.getLength(current);
componentType = currentType.getComponentType();
target = Array.newInstance(componentType, length);
for (int i = 0; i < length; i++) {
Object currentValue = Array.get(current, i);
Object targetValue = this.filter("ARRAY", currentValue);
Array.set(target, i, targetValue);
}
} catch (Exception e) {
log.error("Error creating new array of type "
+ componentType, e);
throwOnNewInstanceException(current, componentType, e);
}
} else {
targetType = findClass(currentType);
if (null == targetType) {
throw new InternalException("Cannot handle type:" + current);
}
try {
target = targetType.newInstance();
} catch (Exception e) {
log.error("Error creating new instance of target type"
+ current, e);
throwOnNewInstanceException(current, targetType, e);
}
}
model2target.put(current, target);
}
return target;
}
public Collection findCollection(Collection source) {
if (source == null) {
return null;
}
Collection target = (Collection) model2target.get(source);
if (null == target) {
if (Set.class.isAssignableFrom(source.getClass())) {
target = new HashSet();
} else if (List.class.isAssignableFrom(source.getClass())) {
target = new ArrayList();
} else {
throw new RuntimeException("Unknown collection type: "
+ source.getClass());
}
model2target.put(source, target);
}
return target;
}
public Map findMap(Map source) {
if (source == null) {
return null;
}
Map target = (Map) model2target.get(source);
if (null == target) {
try {
target = source.getClass().newInstance();
model2target.put(source, target);
} catch (InstantiationException ie) {
throw new RuntimeException(ie);
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
}
}
return target;
}
private void fillTarget(Filterable source, ModelBased target) {
if (source != null && target != null) {
target.copyObject((source), this);
}
}
private void fillCollection(Collection source, Collection target) {
if (source != null && target != null) {
for (Iterator it = source.iterator(); it.hasNext();) {
Object o = it.next();
target.add(this.findCollectionTarget(o));
}
}
}
private void fillMap(Map source, Map target) {
if (source != null && target != null) {
for (Iterator it = source.keySet().iterator(); it.hasNext();) {
Object o = it.next();
target.put(findKeyTarget(o), findCollectionTarget(source.get(o)));
}
}
}
public Timestamp event2timestamp(Event event) {
if (event == null) {
return null;
}
if (!event.isLoaded()) {
return null;
}
if (event.getTime() == null) {
return null;
}
return event.getTime();
}
public int nullSafeInt(Integer i) {
if (i == null) {
return 0;
}
return i.intValue();
}
public long nullSafeLong(Long l) {
if (l == null) {
return 0;
}
return l.longValue();
}
public double nullSafeDouble(Double d) {
if (d == null) {
return 0.0;
}
return d.doubleValue();
}
public float nullSafeFloat(Float f) {
if (f == null) {
return 0.0F;
}
return f.floatValue();
}
// Helpers
// =========================================================================
private void throwOnNewInstanceException(Object current, Class targetType,
Exception e) {
throw new InternalException("Could not instantiate object of type "
+ targetType + " while trying to map " + current + "\n"
+ e.getMessage());
}
public static String stackAsString(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
Throwable cause = t.getCause();
while (cause != null && cause != t) {
cause.printStackTrace(pw);
t = cause;
cause = t.getCause();
}
pw.flush();
pw.close();
return sw.getBuffer().toString();
}
}