package org.mongodb.morphia.query;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.mongodb.morphia.logging.Logger;
import org.mongodb.morphia.logging.MorphiaLoggerFactory;
import org.mongodb.morphia.mapping.MappedClass;
import org.mongodb.morphia.mapping.MappedField;
import org.mongodb.morphia.mapping.Mapper;
import org.mongodb.morphia.utils.ReflectionUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.mongodb.morphia.query.QueryValidator.validateQuery;
class FieldCriteria extends AbstractCriteria {
private static final Logger LOG = MorphiaLoggerFactory.get(FieldCriteria.class);
private final String field;
private final FilterOperator operator;
private final Object value;
private final boolean not;
FieldCriteria(final QueryImpl<?> query, final String field, final FilterOperator op, final Object value) {
this(query, field, op, value, false);
}
@SuppressWarnings("deprecation")
FieldCriteria(final QueryImpl<?> query, final String fieldName, final FilterOperator op, final Object value, final boolean not) {
final StringBuilder sb = new StringBuilder(fieldName);
final Mapper mapper = query.getDatastore().getMapper();
final MappedField mf = validateQuery(query.getEntityClass(),
mapper,
sb,
op,
value,
query.isValidatingNames(),
query.isValidatingTypes());
MappedClass mc = null;
try {
if (value != null && !ReflectionUtils.isPropertyType(value.getClass())
&& !ReflectionUtils.implementsInterface(value.getClass(), Iterable.class)) {
if (mf != null && !mf.isTypeMongoCompatible()) {
mc = mapper.getMappedClass((mf.isSingleValue()) ? mf.getType() : mf.getSubClass());
} else {
mc = mapper.getMappedClass(value);
}
}
} catch (Exception e) {
LOG.debug("Error during mapping of filter criteria: ", e);
}
Object mappedValue = mapper.toMongoObject(mf, mc, value);
final Class<?> type = (mappedValue == null) ? null : mappedValue.getClass();
if (type != null && (op == FilterOperator.IN || op == FilterOperator.NOT_IN)
&& !type.isArray() && !Iterable.class.isAssignableFrom(type)) {
mappedValue = Collections.singletonList(mappedValue);
}
if (value != null && type == null && (op == FilterOperator.IN || op == FilterOperator.NOT_IN)
&& Iterable.class.isAssignableFrom(value.getClass())) {
mappedValue = Collections.emptyList();
}
this.field = sb.toString();
this.operator = op;
this.value = mappedValue;
this.not = not;
}
@Override
@SuppressWarnings("unchecked")
public void addTo(final DBObject obj) {
if (FilterOperator.EQUAL.equals(operator)) {
if (not) {
obj.put(field, new BasicDBObject("$not", value));
} else {
obj.put(field, value);
}
} else {
final Object object = obj.get(field);
Map<String, Object> inner;
if (!(object instanceof Map)) {
inner = new HashMap<String, Object>();
obj.put(field, inner);
} else {
inner = (Map<String, Object>) object;
}
if (not) {
inner.put("$not", new BasicDBObject(operator.val(), value));
} else {
inner.put(operator.val(), value);
}
}
}
@Override
public String getFieldName() {
return field;
}
public String getField() {
return field;
}
public FilterOperator getOperator() {
return operator;
}
public Object getValue() {
return value;
}
public boolean isNot() {
return not;
}
@Override
public String toString() {
return field + " " + operator.val() + " " + value;
}
}