package com.constellio.app.ui.pages.search.criteria;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import java.util.List;
import java.util.ListIterator;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_EmptyCondition;
import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_TooManyClosedParentheses;
import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_UnclosedParentheses;
import com.constellio.app.ui.pages.search.criteria.Criterion.BooleanOperator;
import com.constellio.app.ui.pages.search.criteria.RelativeCriteria.RelativeSearchOperator;
import com.constellio.data.utils.TimeProvider;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.model.services.search.query.logical.criteria.MeasuringUnitTime;
public class ConditionBuilder {
private MetadataSchemaType schemaType;
private String languageCode;
public ConditionBuilder(MetadataSchemaType schemaType, String languageCode) {
this.schemaType = schemaType;
this.languageCode = languageCode;
}
public LogicalSearchCondition build(List<Criterion> criteria)
throws ConditionException {
if (criteria.isEmpty()) {
throw new ConditionException_EmptyCondition();
}
return buildOuterCondition(criteria.listIterator());
}
private LogicalSearchCondition buildOuterCondition(ListIterator<Criterion> criteria)
throws ConditionException {
ConditionAppender appender = new ConditionAppender(schemaType);
BooleanOperator operator = BooleanOperator.AND;
while (criteria.hasNext()) {
Criterion criterion = criteria.next();
if (criterion.isLeftParens()) {
appender.append(buildInnerCondition(criterion, criteria), operator);
operator = criteria.previous().getBooleanOperator();
criteria.next();
} else if (criterion.isRightParens()) {
throw new ConditionException_TooManyClosedParentheses();
} else {
appender.append(buildClause(criterion), operator);
operator = criterion.getBooleanOperator();
}
}
return appender.build();
}
private LogicalSearchCondition buildInnerCondition(Criterion first, ListIterator<Criterion> criteria)
throws ConditionException {
ConditionAppender appender = new ConditionAppender(schemaType);
appender.append(buildClause(first), BooleanOperator.AND);
BooleanOperator operator = first.getBooleanOperator();
while (criteria.hasNext()) {
Criterion criterion = criteria.next();
if (criterion.isLeftParens()) {
appender.append(buildInnerCondition(criterion, criteria), operator);
operator = criteria.previous().getBooleanOperator();
criteria.next();
} else {
appender.append(buildClause(criterion), operator);
operator = criterion.getBooleanOperator();
}
if (criterion.isRightParens()) {
return appender.build();
}
}
throw new ConditionException_UnclosedParentheses();
}
private LogicalSearchCondition buildClause(Criterion criterion) {
Metadata metadata = schemaType.getMetadata(criterion.getMetadataCode());
Object value;
Object endValue;
switch (criterion.getSearchOperator()) {
case EQUALS:
value = getValue(criterion, metadata, false);
return from(schemaType).where(metadata).isEqualTo(value);
case CONTAINS_TEXT:
String stringValue = (String) criterion.getValue();
if (metadata.getType() == MetadataValueType.STRING && !metadata.isSearchable()) {
return from(schemaType).where(metadata).isContainingText(stringValue);
} else {
return from(schemaType).where(metadata.getAnalyzedField(languageCode)).query(stringValue.replace("-", "\\-"));
}
case LESSER_THAN:
value = getValue(criterion, metadata, false);
return from(schemaType).where(metadata).isLessThan(value);
case GREATER_THAN:
value = getValue(criterion, metadata, false);
return from(schemaType).where(metadata).isGreaterThan(value);
case BETWEEN:
value = getValue(criterion, metadata, false);
endValue = getValue(criterion, metadata, true);
return from(schemaType).where(metadata).isValueInRange(value, endValue);
case IS_TRUE:
return from(schemaType).where(metadata).isTrue();
case IS_FALSE:
return from(schemaType).where(metadata).isFalseOrNull();
case IN_HIERARCHY:
return from(schemaType).where(Schemas.PATH).isContainingText("/" + criterion.getValue() + "/");
case IS_NULL:
return from(schemaType).where(metadata).isNull();
case IS_NOT_NULL:
return from(schemaType).where(metadata).isNotNull();
default:
throw new RuntimeException("Unsupported search operator");
}
}
private Object getValue(Criterion criterion, Metadata metadata, boolean isEndValue) {
Object value;
if (metadata.getType().equals(MetadataValueType.DATE_TIME) || metadata.getType().equals(MetadataValueType.DATE)) {
value = getDateValue(criterion, isEndValue);
} else {
if (isEndValue) {
value = criterion.getEndValue();
} else {
value = criterion.getValue();
}
}
return value;
}
private LocalDate getDateValue(Criterion criterion, boolean isEndValue) {
LocalDate dateValue = null;
LocalDate now = TimeProvider.getLocalDate();
RelativeSearchOperator relativeSearchOperator;
MeasuringUnitTime measuringUnitTime;
Object value;
if (isEndValue) {
relativeSearchOperator = criterion.getRelativeCriteria().getEndRelativeSearchOperator();
measuringUnitTime = criterion.getRelativeCriteria().getEndMeasuringUnitTime();
value = criterion.getEndValue();
} else {
relativeSearchOperator = criterion.getRelativeCriteria().getRelativeSearchOperator();
measuringUnitTime = criterion.getRelativeCriteria().getMeasuringUnitTime();
value = criterion.getValue();
}
if (relativeSearchOperator == RelativeSearchOperator.TODAY) {
dateValue = now;
} else if (relativeSearchOperator == RelativeSearchOperator.EQUALS) {
if (value instanceof LocalDateTime) {
dateValue = ((LocalDateTime) value).toLocalDate();
} else {
dateValue = (LocalDate) value;
}
} else if (relativeSearchOperator == RelativeSearchOperator.PAST) {
int intValue = ((Double) value).intValue();
if (measuringUnitTime == MeasuringUnitTime.DAYS) {
dateValue = now.minusDays(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.WEEKS) {
dateValue = now.minusWeeks(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.MONTHS) {
dateValue = now.minusMonths(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.YEARS) {
dateValue = now.minusYears(intValue);
}
} else if (relativeSearchOperator == RelativeSearchOperator.FUTURE) {
int intValue = ((Double) value).intValue();
if (measuringUnitTime == MeasuringUnitTime.DAYS) {
dateValue = now.plusDays(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.WEEKS) {
dateValue = now.plusWeeks(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.MONTHS) {
dateValue = now.plusMonths(intValue);
} else if (measuringUnitTime == MeasuringUnitTime.YEARS) {
dateValue = now.plusYears(intValue);
}
}
return dateValue;
}
}