org.mongodb.morphia.converters.Converters Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of morphia Show documentation
Show all versions of morphia Show documentation
Java Object Document Mapper for MongoDB
The newest version!
package org.mongodb.morphia.converters;
import com.mongodb.DBObject;
import org.mongodb.morphia.logging.Logger;
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
import org.mongodb.morphia.mapping.MappedField;
import org.mongodb.morphia.mapping.Mapper;
import org.mongodb.morphia.mapping.MapperOptions;
import org.mongodb.morphia.mapping.MappingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.String.format;
/**
* Defines a bundle of converters
*/
public abstract class Converters {
private static final Logger LOG = MorphiaLoggerFactory.get(Converters.class);
private final Mapper mapper;
private final List untypedTypeEncoders = new LinkedList();
private final Map> tcMap = new ConcurrentHashMap>();
private final List> registeredConverterClasses = new ArrayList>();
/**
* Creates a bundle with a particular Mapper.
*
* @param mapper the Mapper to use
*/
public Converters(final Mapper mapper) {
this.mapper = mapper;
}
/**
* Adds a TypeConverter to this bundle.
*
* @param clazz the converter to add
* @return the new instance
*/
public TypeConverter addConverter(final Class clazz) {
return addConverter(mapper.getOptions().getObjectFactory().createInstance(clazz));
}
/**
* Add a type converter. If it is a duplicate for an existing type, it will override that type.
*
* @param tc the converter to add
* @return the TypeConverter passed in
*/
public TypeConverter addConverter(final TypeConverter tc) {
if (tc.getSupportedTypes() != null) {
for (final Class c : tc.getSupportedTypes()) {
addTypedConverter(c, tc);
}
} else {
untypedTypeEncoders.add(tc);
}
registeredConverterClasses.add(tc.getClass());
tc.setMapper(mapper);
return tc;
}
/**
* decode the {@link com.mongodb.DBObject} and provide the corresponding java (type-safe) object
*
NOTE: mf might be null
*
* @param c the class to create and populate
* @param fromDBObject the DBObject to use when populating the new instance
* @param mf the MappedField that contains the metadata useful for decoding
* @return the new instance
*/
public Object decode(final Class c, final Object fromDBObject, final MappedField mf) {
Class toDecode = c;
if (toDecode == null) {
toDecode = fromDBObject.getClass();
}
return getEncoder(toDecode).decode(toDecode, fromDBObject, mf);
}
/**
* encode the type safe java object into the corresponding {@link com.mongodb.DBObject}
*
* @param o The object to encode
* @return the encoded version of the object
*/
public Object encode(final Object o) {
if (o == null) {
return null;
}
return encode(o.getClass(), o);
}
/**
* encode the type safe java object into the corresponding {@link com.mongodb.DBObject}
*
* @param c The type to use when encoding
* @param o The object to encode
* @return the encoded version of the object
*/
public Object encode(final Class c, final Object o) {
return getEncoder(c).encode(o);
}
/**
* Creates an entity and populates its state based on the dbObject given. This method is primarily an internal method. Reliance on
* this method may break your application in future releases.
*
* @param dbObj the object state to use
* @param mf the MappedField containing the metadata to use when decoding in to a field
* @param targetEntity then entity to hold the state from the database
*/
public void fromDBObject(final DBObject dbObj, final MappedField mf, final Object targetEntity) {
final Object object = mf.getDbObjectValue(dbObj);
if (object != null) {
final TypeConverter enc = getEncoder(mf);
final Object decodedValue = enc.decode(mf.getType(), object, mf);
try {
mf.setFieldValue(targetEntity, decodedValue);
} catch (IllegalArgumentException e) {
throw new MappingException(format("Error setting value from converter (%s) for %s to %s",
enc.getClass().getSimpleName(), mf.getFullName(), decodedValue), e);
}
}
}
/**
* @param field the field to check with
* @return true if there is a converter for the type of the field
*/
public boolean hasDbObjectConverter(final MappedField field) {
final TypeConverter converter = getEncoder(field);
return converter != null && !(converter instanceof IdentityConverter) && !(converter instanceof SimpleValueConverter);
}
/**
* @param c the type to check
* @return true if there is a converter for the type
*/
public boolean hasDbObjectConverter(final Class c) {
final TypeConverter converter = getEncoder(c);
return converter != null && !(converter instanceof IdentityConverter) && !(converter instanceof SimpleValueConverter);
}
/**
* @param o the object/type to check
* @return true if there is a SimpleValueConverter for the type represented by o
* @see SimpleValueConverter
*/
public boolean hasSimpleValueConverter(final Object o) {
if (o == null) {
return false;
}
if (o instanceof Class) {
return hasSimpleValueConverter((Class) o);
} else if (o instanceof MappedField) {
return hasSimpleValueConverter((MappedField) o);
} else {
return hasSimpleValueConverter(o.getClass());
}
}
/**
* @param c the type to check
* @return true if there is a SimpleValueConverter for the type represented by c
* @see SimpleValueConverter
*/
public boolean hasSimpleValueConverter(final Class c) {
return (getEncoder(c) instanceof SimpleValueConverter);
}
/**
* @param c the type to check
* @return true if there is a SimpleValueConverter for the type represented by c
* @see SimpleValueConverter
*/
public boolean hasSimpleValueConverter(final MappedField c) {
return (getEncoder(c) instanceof SimpleValueConverter);
}
/**
* @param tcClass the type to check
* @return true if a converter of this type has been registered
*/
public boolean isRegistered(final Class tcClass) {
return registeredConverterClasses.contains(tcClass);
}
/**
* Removes the type converter.
*
* @param tc the converter to remove
*/
public void removeConverter(final TypeConverter tc) {
if (tc.getSupportedTypes() == null) {
untypedTypeEncoders.remove(tc);
registeredConverterClasses.remove(tc.getClass());
} else {
for (final Entry> entry : tcMap.entrySet()) {
List list = entry.getValue();
if (list.contains(tc)) {
list.remove(tc);
}
if (list.isEmpty()) {
tcMap.remove(entry.getKey());
}
}
registeredConverterClasses.remove(tc.getClass());
}
}
/**
* Converts an entity to a DBObject
*
* @param containingObject The object to convert
* @param mf the MappedField to extract
* @param dbObj the DBObject to populate
* @param opts the options to apply
*/
public void toDBObject(final Object containingObject, final MappedField mf, final DBObject dbObj, final MapperOptions opts) {
final Object fieldValue = mf.getFieldValue(containingObject);
final TypeConverter enc = getEncoder(fieldValue, mf);
final Object encoded = enc.encode(fieldValue, mf);
if (encoded != null || opts.isStoreNulls()) {
dbObj.put(mf.getNameToStore(), encoded);
}
}
protected TypeConverter getEncoder(final Class c) {
final List tcs = tcMap.get(c);
if (tcs != null) {
if (tcs.size() > 1) {
LOG.warning("Duplicate converter for " + c + ", returning first one from " + tcs);
}
return tcs.get(0);
}
for (final TypeConverter tc : untypedTypeEncoders) {
if (tc.canHandle(c)) {
return tc;
}
}
return null;
}
protected TypeConverter getEncoder(final Object val, final MappedField mf) {
List tcs = null;
if (val != null) {
tcs = tcMap.get(val.getClass());
}
if (tcs == null || (!tcs.isEmpty() && tcs.get(0) instanceof IdentityConverter)) {
tcs = tcMap.get(mf.getType());
}
if (tcs != null) {
if (tcs.size() > 1) {
LOG.warning("Duplicate converter for " + mf.getType() + ", returning first one from " + tcs);
}
return tcs.get(0);
}
for (final TypeConverter tc : untypedTypeEncoders) {
if (tc.canHandle(mf) || (val != null && tc.isSupported(val.getClass(), mf))) {
return tc;
}
}
return null;
}
private void addTypedConverter(final Class type, final TypeConverter tc) {
if (tcMap.containsKey(type)) {
tcMap.get(type).add(0, tc);
LOG.warning("Added duplicate converter for " + type + " ; " + tcMap.get(type));
} else {
final List values = new ArrayList();
values.add(tc);
tcMap.put(type, values);
}
}
private TypeConverter getEncoder(final MappedField mf) {
return getEncoder(null, mf);
}
}