Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (C) 2010 Olafur Gauti Gudmundsson Licensed under the Apache
* License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.google.code.morphia.mapping;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Pattern;
import org.bson.BSONEncoder;
import org.bson.BasicBSONEncoder;
import com.google.code.morphia.EntityInterceptor;
import com.google.code.morphia.Key;
import com.google.code.morphia.annotations.Converters;
import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;
import com.google.code.morphia.annotations.NotSaved;
import com.google.code.morphia.annotations.PostLoad;
import com.google.code.morphia.annotations.PreLoad;
import com.google.code.morphia.annotations.PrePersist;
import com.google.code.morphia.annotations.PreSave;
import com.google.code.morphia.annotations.Property;
import com.google.code.morphia.annotations.Reference;
import com.google.code.morphia.annotations.Serialized;
import com.google.code.morphia.converters.DefaultConverters;
import com.google.code.morphia.converters.TypeConverter;
import com.google.code.morphia.logging.Logr;
import com.google.code.morphia.logging.MorphiaLoggerFactory;
import com.google.code.morphia.mapping.cache.DefaultEntityCache;
import com.google.code.morphia.mapping.cache.EntityCache;
import com.google.code.morphia.mapping.lazy.DatastoreProvider;
import com.google.code.morphia.mapping.lazy.DefaultDatastoreProvider;
import com.google.code.morphia.mapping.lazy.LazyFeatureDependencies;
import com.google.code.morphia.mapping.lazy.LazyProxyFactory;
import com.google.code.morphia.mapping.lazy.proxy.ProxiedEntityReference;
import com.google.code.morphia.mapping.lazy.proxy.ProxyHelper;
import com.google.code.morphia.query.FilterOperator;
import com.google.code.morphia.query.ValidationException;
import com.google.code.morphia.utils.ReflectionUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
*
This is the heart of Morphia and takes care of mapping from/to POJOs/DBObjects
This class is thread-safe and keeps various
* "cached" data which should speed up processing.
*
* @author Olafur Gauti Gudmundsson
* @author Scott Hernandez
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class Mapper {
private static final Logr log = MorphiaLoggerFactory.get(Mapper.class);
/**
* The @{@link Id} field name that is stored with mongodb.
*/
public static final String ID_KEY = "_id";
/**
* Special name that can never be used. Used as default for some fields to indicate default state.
*/
public static final String IGNORED_FIELDNAME = ".";
/**
* Special field used by morphia to support various possibly loading issues; will be replaced when discriminators are implemented to
* support polymorphism
*/
public static final String CLASS_NAME_FIELDNAME = "className";
/**
* Set of classes that registered by this mapper
*/
private final Map mappedClasses = new ConcurrentHashMap();
private final ConcurrentHashMap> mappedClassesByCollection = new ConcurrentHashMap>();
//EntityInterceptors; these are called before EntityListeners and lifecycle methods on an Entity, for all Entities
private final List interceptors = new LinkedList();
//A general cache of instances of classes; used by MappedClass for EntityListener(s)
final Map instanceCache = new ConcurrentHashMap();
private MapperOptions opts = new MapperOptions();
// TODO: make these configurable
final LazyProxyFactory proxyFactory = LazyFeatureDependencies.createDefaultProxyFactory();
final DatastoreProvider datastoreProvider = new DefaultDatastoreProvider();
final DefaultConverters converters = new DefaultConverters();
public Mapper() {
converters.setMapper(this);
}
public Mapper(final MapperOptions opts) {
this();
this.opts = opts;
}
/**
*
*/
public Collection getInterceptors() {
return interceptors;
}
public MapperOptions getOptions() {
return opts;
}
public void setOptions(final MapperOptions options) {
opts = options;
}
public boolean isMapped(final Class c) {
return mappedClasses.containsKey(c.getName());
}
/**
* Creates a MappedClass and validates it.
*/
public MappedClass addMappedClass(final Class c) {
return addMappedClass(new MappedClass(c, this), true);
}
/**
* Validates MappedClass and adds to internal cache.
*/
public MappedClass addMappedClass(final MappedClass mc) {
return addMappedClass(mc, true);
}
/**
* Add MappedClass to internal cache, possibly validating first.
*/
private MappedClass addMappedClass(final MappedClass mc, final boolean validate) {
if (validate) {
mc.validate();
}
final Converters c = (Converters) mc.getAnnotation(Converters.class);
if (c != null) {
for (final Class extends TypeConverter> clazz : c.value()) {
if (!converters.isRegistered(clazz)) {
converters.addConverter(clazz);
}
}
}
mappedClasses.put(mc.getClazz().getName(), mc);
Set mcs = mappedClassesByCollection.get(mc.getCollectionName());
if (mcs == null) {
mcs = new CopyOnWriteArraySet();
final Set temp = mappedClassesByCollection.putIfAbsent(mc.getCollectionName(), mcs);
if (temp != null) {
mcs = temp;
}
}
mcs.add(mc);
return mc;
}
/**
* Returns collection of MappedClasses
*/
public Collection getMappedClasses() {
return new ArrayList(mappedClasses.values());
}
/**
* Returns map of MappedClasses by class name
*/
public Map getMCMap() {
return Collections.unmodifiableMap(mappedClasses);
}
/**
*
Gets the {@link MappedClass} for the object (type). If it isn't mapped, create a new class and cache it (without validating).
*/
public MappedClass getMappedClass(final Object obj) {
if (obj == null) {
return null;
}
Class type = (obj instanceof Class) ? (Class) obj : obj.getClass();
if (ProxyHelper.isProxy(obj)) {
type = ProxyHelper.getReferentClass(obj);
}
MappedClass mc = mappedClasses.get(type.getName());
if (mc == null) {
mc = new MappedClass(type, this);
// no validation
addMappedClass(mc, false);
}
return mc;
}
public String getCollectionName(final Object object) {
if (object == null) {
throw new IllegalArgumentException();
}
final MappedClass mc = getMappedClass(object);
return mc.getCollectionName();
}
/**
*
Updates the @{@link Id} fields.
*
* @param entity The object to update
* @param dbObj Value to update with; null means skip
*/
public void updateKeyInfo(final Object entity, final DBObject dbObj, final EntityCache cache) {
final MappedClass mc = getMappedClass(entity);
// update id field, if there.
if ((mc.getIdField() != null) && (dbObj != null) && (dbObj.get(ID_KEY) != null)) {
try {
final MappedField mf = mc.getMappedIdField();
final Object oldIdValue = mc.getIdField().get(entity);
readMappedField(dbObj, mf, entity, cache);
final Object dbIdValue = mc.getIdField().get(entity);
if (oldIdValue != null) {
// The entity already had an id set. Check to make sure it
// hasn't changed. That would be unexpected, and could
// indicate a bad state.
if (!dbIdValue.equals(oldIdValue)) {
mf.setFieldValue(entity, oldIdValue);//put the value back...
throw new RuntimeException(
"@Id mismatch: " + oldIdValue + " != " + dbIdValue + " for " + entity.getClass().getName());
}
} else {
mc.getIdField().set(entity, dbIdValue);
}
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException("Error setting @Id field after save/insert.", e);
}
}
}
/**
* Converts a DBObject back to a type-safe java object (POJO)
*
* @param entityClass The type to return, or use; can be overridden by the @see Mapper.CLASS_NAME_FIELDNAME in the DBObject
*/
public Object fromDBObject(final Class entityClass, final DBObject dbObject, final EntityCache cache) {
if (dbObject == null) {
final Throwable t = new Throwable();
log.error("Somebody passed in a null dbObject; bad client!", t);
return null;
}
Object entity;
entity = opts.objectFactory.createInstance(entityClass, dbObject);
entity = fromDb(dbObject, entity, cache);
return entity;
}
/**
*
Converts a java object to a mongo-compatible object (possibly a DBObject for complex mappings). Very similar to {@link
* Mapper#toDBObject}
Used (mainly) by query/update operations
*/
Object toMongoObject(final Object javaObj, final boolean includeClassName) {
if (javaObj == null) {
return null;
}
Class origClass = javaObj.getClass();
if (origClass.isAnonymousClass() && origClass.getSuperclass().isEnum()) {
origClass = origClass.getSuperclass();
}
final Object newObj = converters.encode(origClass, javaObj);
if (newObj == null) {
log.warning("converted " + javaObj + " to null");
return newObj;
}
final Class type = newObj.getClass();
final boolean bSameType = origClass.equals(type);
//TODO: think about this logic a bit more.
//Even if the converter changed it, should it still be processed?
if (!bSameType && !(Map.class.isAssignableFrom(type) || Iterable.class.isAssignableFrom(type))) {
return newObj;
} else { //The converter ran, and produced another type, or it is a list/map
boolean isSingleValue = true;
boolean isMap = false;
Class subType = null;
if (type.isArray() || Map.class.isAssignableFrom(type) || Iterable.class.isAssignableFrom(type)) {
isSingleValue = false;
isMap = ReflectionUtils.implementsInterface(type, Map.class);
// subtype of Long[], List is Long
subType = (type.isArray()) ? type.getComponentType() : ReflectionUtils.getParameterizedClass(type, (isMap) ? 1 : 0);
}
if (isSingleValue && !ReflectionUtils.isPropertyType(type)) {
final DBObject dbObj = toDBObject(newObj);
if (!includeClassName) {
dbObj.removeField(CLASS_NAME_FIELDNAME);
}
return dbObj;
} else if (newObj instanceof DBObject) {
return newObj;
} else if (isMap) {
if (ReflectionUtils.isPropertyType(subType)) {
return toDBObject(newObj);
} else {
final HashMap m = new HashMap();
for (final Map.Entry e : (Iterable) ((Map) newObj).entrySet()) {
m.put(e.getKey(), toMongoObject(e.getValue(), includeClassName));
}
return m;
}
//Set/List but needs elements converted
} else if (!isSingleValue && !ReflectionUtils.isPropertyType(subType)) {
final List