package ru.hflabs.rcd.storage;
import com.google.common.collect.Sets;
import org.springframework.util.StringUtils;
import ru.hflabs.rcd.model.Identifying;
import ru.hflabs.rcd.model.criteria.CriteriaHolder;
import ru.hflabs.rcd.model.criteria.FilterActivity;
import ru.hflabs.rcd.model.criteria.FilterCriteria;
import ru.hflabs.rcd.model.criteria.FilterCriteriaValue;
import ru.hflabs.rcd.service.IActivityBuilder;
import ru.hflabs.rcd.service.ICriteriaBuilder;
import ru.hflabs.rcd.term.Condition;
import javax.swing.*;
import java.util.*;
import java.util.regex.Pattern;
/**
* Класс <class>CriteriaBuilderTemplate</class> базовый класс для сервиса построения критерии
*
* @see FilterCriteria
*/
public abstract class CriteriaBuilderTemplate<E extends Identifying, C extends CriteriaHolder<Q>, Q> implements ICriteriaBuilder<E, C, Q> {
/** Регулярное выражение для определения пустых символов */
private static final Pattern SPACE_PATTERN = Pattern.compile("\\s+");
/** Делегат построения активности документов */
private Collection<IActivityBuilder<E, C, Q>> activityBuilders;
protected CriteriaBuilderTemplate() {
this.activityBuilders = Collections.emptyList();
}
public void setActivityBuilders(Collection<IActivityBuilder<E, C, Q>> activityBuilders) {
this.activityBuilders = activityBuilders;
}
/**
* Создает и вызывает исключтельную ситуацию нереализованного метода
*
* @return Вызывает исключительную ситуацию нереализованного метода проверки
*/
protected <K> K throwAssertionError(String action) {
throw new UnsupportedOperationException(String.format("%s not supported by '%s'", action, getClass().getName()));
}
/**
* Выполняет формирование критерии для пустых значений
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByEmptyValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.EmptyValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для не пустых значений
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByNotEmptyValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.NotEmptyValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для логического типа
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByBooleanValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.BooleanValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для перечисляемого типа
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByEnumValues(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.EnumValues<?> value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для строки
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByStringValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.StringValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для даты
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByDateValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.DateValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет формирование критерии для коллекции строки
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param criteriaField целевое поле
* @param value значение фильтра
* @return Возвращает обновленный критерий
*/
protected C appendFilterByStringsValue(C current, Class<E> criteriaClass, String criteriaField, FilterCriteriaValue.StringsValue value) {
return throwAssertionError(value.getClass().getName());
}
/**
* Выполняет построение критерии поиска по умолчанию
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param values коллекция значений поиска
* @param condition условие ассоциации поиска с фильтрами
* @return Возвращает обновленный критерий
*/
protected C appendSearch(C current, Class<E> criteriaClass, Set<String> values, Condition condition) {
return throwAssertionError("search by default value");
}
/**
* Выполняет построение критерии поиска по умолчанию
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param value значение поиска
* @param condition условие ассоциации поиска с фильтрами
* @return Возвращает обновленный критерий
*/
protected C appendSearch(C current, Class<E> criteriaClass, String value, Condition condition) {
if (StringUtils.hasText(value)) {
// Выполняем выделение отдельных слов из строки поиска
Set<String> words = Sets.newLinkedHashSet(Arrays.asList(SPACE_PATTERN.split(value)));
// Выполняем построение фильтров
current = appendSearch(current, criteriaClass, words, condition);
}
return current;
}
/**
* Выполняет построение фильтров критерии
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param filters коллекция фильтров
* @return Возвращает обновленный критерий
*/
protected C appendFilters(C current, Class<E> criteriaClass, Map<String, FilterCriteriaValue<?>> filters) {
// Выполняем построение фильтров
if (filters != null) {
for (Map.Entry<String, FilterCriteriaValue<?>> entry : filters.entrySet()) {
// Получаем клю фильтра
final String key = entry.getKey();
// Получаем значение фильтра
final FilterCriteriaValue<?> value = entry.getValue();
// Определяем тип запроса
if (value instanceof FilterCriteriaValue.ClauseValue) {
C innerClauseCriteria = createEmptyCriteria(criteriaClass);
innerClauseCriteria = appendFilters(innerClauseCriteria, criteriaClass, ((FilterCriteriaValue.ClauseValue) value).getValue());
current.appendQuery(innerClauseCriteria.buildQuery(), value.getCondition());
} else if (value instanceof FilterCriteriaValue.EmptyValue) {
current = appendFilterByEmptyValue(current, criteriaClass, key, (FilterCriteriaValue.EmptyValue) value);
} else if (value instanceof FilterCriteriaValue.NotEmptyValue) {
current = appendFilterByNotEmptyValue(current, criteriaClass, key, (FilterCriteriaValue.NotEmptyValue) value);
} else if (value instanceof FilterCriteriaValue.BooleanValue) {
current = appendFilterByBooleanValue(current, criteriaClass, key, (FilterCriteriaValue.BooleanValue) value);
} else if (value instanceof FilterCriteriaValue.EnumValues) {
current = appendFilterByEnumValues(current, criteriaClass, key, (FilterCriteriaValue.EnumValues<?>) value);
} else if (value instanceof FilterCriteriaValue.StringValue) {
current = appendFilterByStringValue(current, criteriaClass, key, (FilterCriteriaValue.StringValue) value);
} else if (value instanceof FilterCriteriaValue.DateValue) {
current = appendFilterByDateValue(current, criteriaClass, key, (FilterCriteriaValue.DateValue) value);
} else if (value instanceof FilterCriteriaValue.StringsValue) {
current = appendFilterByStringsValue(current, criteriaClass, key, (FilterCriteriaValue.StringsValue) value);
} else {
throwAssertionError(value.getClass().getName());
}
}
}
// Возвращаем модифицированный критерий
return current;
}
/**
* Выполняет построение сортировки критерии
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param orderKey поле по которому производится сортировка
* @param orderValue значение сортировки
* @return Возвращает обновленный критерий
*/
protected C appendOrder(C current, Class<E> criteriaClass, String orderKey, SortOrder orderValue) {
return throwAssertionError(SortOrder.class.getSimpleName());
}
/**
* Добавляет сортировку по умолчанию
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @return Возвращает обновленный критерий
*/
protected C appendDefaultOrder(C current, Class<E> criteriaClass) {
return current;
}
/**
* Выполняет построение сортировки критерии
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param orderKey поле по которому производится сортировка
* @param orderValue значение сортировки
* @return Возвращает обновленный критерий
*/
protected C appendOrders(C current, Class<E> criteriaClass, String orderKey, SortOrder orderValue) {
if (StringUtils.hasText(orderKey) && !SortOrder.UNSORTED.equals(orderValue)) {
current = appendOrder(current, criteriaClass, orderKey, orderValue);
}
return appendDefaultOrder(current, criteriaClass);
}
/**
* Выполняет построение фильтра активности документов
*
* @param current текущий критерий
* @param criteriaClass целевой класс критерии
* @param activity целевая активность документов
* @return Возвращает обновленный критерий
*/
protected C appendActivity(C current, Class<E> criteriaClass, FilterActivity activity) {
for (IActivityBuilder<E, C, Q> builder : activityBuilders) {
if (builder.isSupport(criteriaClass)) {
current = builder.createActivity(current, criteriaClass, activity);
}
}
return current;
}
@Override
public C createCriteria(Class<E> criteriaClass, FilterCriteria filter) {
C criteria = createEmptyCriteria(criteriaClass);
{
criteria = appendSearch(criteria, criteriaClass, filter.getSearch(), filter.getSearchCondition());
criteria = appendFilters(criteria, criteriaClass, filter.getFilters());
criteria = appendOrders(criteria, criteriaClass, filter.getSortOrderKey(), filter.getSortOrderValue());
criteria = appendActivity(criteria, criteriaClass, filter.getActivity());
}
return criteria;
}
}