package ru.hflabs.rcd.web.controller.rule;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import ru.hflabs.rcd.exception.constraint.IllegalPrimaryKeyException;
import ru.hflabs.rcd.model.criteria.FilterCriteria;
import ru.hflabs.rcd.model.criteria.FilterCriteriaValue;
import ru.hflabs.rcd.model.definition.ModelDefinition;
import ru.hflabs.rcd.model.document.MetaField;
import ru.hflabs.rcd.model.rule.RecodeRuleSet;
import ru.hflabs.rcd.service.document.IFieldService;
import ru.hflabs.rcd.service.document.IMetaFieldService;
import ru.hflabs.rcd.service.rule.IRecodeRuleSetService;
import ru.hflabs.rcd.web.controller.ControllerTemplate;
import ru.hflabs.rcd.web.model.rule.RecodeRuleDirection;
import ru.hflabs.rcd.web.model.rule.RecodeRuleSetRequestBean;
import ru.hflabs.rcd.web.model.rule.RecodeRuleSetResponseBean;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import static ru.hflabs.rcd.accessor.Accessors.*;
import static ru.hflabs.rcd.model.ModelUtils.ID_FUNCTION;
import static ru.hflabs.rcd.model.change.Predicates.*;
/**
* Класс <class>RecodeRuleSetController</class> реализует контроллер управления наборами правил перекодирования
*
* @see RecodeRuleSet
*/
@Controller(RecodeRuleSetController.MAPPING_URI + RecodeRuleSetController.NAME_POSTFIX)
@RequestMapping(RecodeRuleSetController.MAPPING_URI + RecodeRuleSetController.DATA_URI)
public class RecodeRuleSetController extends ControllerTemplate {
public static final String MAPPING_URI = "rrs";
/** Сервис работы с МЕТА-полями справочника */
@Resource(name = "metaFieldService")
private IMetaFieldService metaFieldService;
/** Сервис работы со значениями полей справочника */
@Resource(name = "fieldService")
private IFieldService fieldService;
/** Сервис работы с наборами правил перекодирования */
@Resource(name = "recodeRuleSetService")
private IRecodeRuleSetService recodeRuleSetService;
@RequestMapping(value = "/model", method = RequestMethod.GET)
@ResponseBody
public ModelDefinition createModel() {
return modelDefinitionFactory.retrieveService(RecodeRuleSetRequestBean.class);
}
@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public Collection<RecodeRuleSetResponseBean> getRecodeRuleSets(
@RequestParam(value = MetaField.DICTIONARY_ID, required = false) String dictionaryId,
@RequestParam(value = "direction", required = false) RecodeRuleDirection direction) {
FilterCriteria filterCriteria = new FilterCriteria();
// Устанавливаем идентификатор справочника, если он задан
if (StringUtils.hasText(dictionaryId)) {
final String targetFieldName;
if (RecodeRuleDirection.FROM.equals(direction)) {
targetFieldName = RecodeRuleSet.FROM_DICTIONARY_ID;
} else if (RecodeRuleDirection.TO.equals(direction)) {
targetFieldName = RecodeRuleSet.TO_DICTIONARY_ID;
} else {
targetFieldName = MetaField.DICTIONARY_ID;
}
filterCriteria.injectFilters(
ImmutableMap.<String, FilterCriteriaValue<?>>of(
targetFieldName, new FilterCriteriaValue.StringValue(dictionaryId)
)
);
}
// Выполняем поиск
return Collections2.transform(
recodeRuleSetService.findAllByCriteria(filterCriteria, true),
RecodeRuleSetResponseBean.CONVERT
);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public RecodeRuleSetResponseBean getRecodeRuleSet(@PathVariable String id) {
return RecodeRuleSetResponseBean.CONVERT.apply(
recodeRuleSetService.findByID(id, true, false)
);
}
private static MetaField detectRecodeMetaField(FluentIterable<MetaField> metaFields, Predicate<MetaField> predicate) {
return metaFields.firstMatch(predicate).orNull();
}
/**
* Определяет МЕТА-поле перекодирования по следующему принципу:</br>
* <ul>
* <li>{@link MetaField#FLAG_PRIMARY Первичное} поле, если оно {@link MetaField#FLAG_HIDDEN не скрытое}</li>
* <li>Первое {@link MetaField#FLAG_UNIQUE уникальное} и не скрытое поле</li>
* <li>Первое не скрытое поле, из коллекции, отсортированной по {@link MetaField#ordinal порядку}</li>
* </ul>
*
* @param metaFieldId идентификатор предпочитаемого поля или <code>NULL</code>
* @param dictionaryId идентификатор справочника
* @return Возвращает МЕТА-поле
*/
private MetaField detectRecodeMetaField(String metaFieldId, String dictionaryId) {
if (StringUtils.hasText(metaFieldId)) {
return metaFieldService.findByID(metaFieldId, true, false);
}
// Получаем все МЕТА-поля справочника
FluentIterable<MetaField> metaFields = FluentIterable.from(metaFieldService.findAllByRelativeId(dictionaryId, null, true));
// Пытаемся получить первичное МЕТА-поле
MetaField result = detectRecodeMetaField(metaFields, Predicates.and(PRIMARY_META_FIELD_PREDICATE, NOT_HIDDEN_META_FIELD_PREDICATE));
if (result != null) {
return result;
}
// Пытаемся получить уникальное МЕТА-поле
result = detectRecodeMetaField(metaFields, Predicates.and(UNIQUE_META_FIELD_PREDICATE, NOT_HIDDEN_META_FIELD_PREDICATE));
if (result != null) {
return result;
}
// Получаем первое не скрытое
result = detectRecodeMetaField(metaFields, NOT_HIDDEN_META_FIELD_PREDICATE);
if (result != null) {
return result;
}
// Определить МЕТА-поле не удалось, ориентируемся на первичное
result = detectRecodeMetaField(metaFields, PRIMARY_META_FIELD_PREDICATE);
if (result != null) {
return result;
}
// Определить целевое МЕТА-поле не удалось
throw new IllegalPrimaryKeyException(
String.format("Can't find meta fields for dictionary with ID '%s'", dictionaryId)
);
}
/**
* Выполняет формирование набора правил перекодирования из идентификаторов связанных сущностей
*
* @param id идентификатор правила
* @param bean декоратор идентификаторов
* @return Возвращает сфориированный набор правил перекодирования
*/
private RecodeRuleSet convertRecodeRuleSet(String id, RecodeRuleSetRequestBean bean) {
// Получаем исходное МЕТА-поле
MetaField fromMetaField = detectRecodeMetaField(bean.getFromMetaFieldId(), bean.getFromDictionaryId());
// Получаем целевое МЕТА-поле
MetaField toMetaField = detectRecodeMetaField(bean.getToMetaFieldId(), bean.getToDictionaryId());
// Формируем правило перекодирования
RecodeRuleSet ruleSet = injectId(bean.getDelegate(), id);
ruleSet = FROM_SET_INJECTOR.inject(ruleSet, fromMetaField);
ruleSet = TO_SET_INJECTOR.inject(ruleSet, toMetaField);
// Формируем идентификатор поля поумолчанию
if (StringUtils.hasText(bean.getDefaultRecordId())) {
ruleSet = ruleSet.injectDefaultField(
fieldService.findUniqueByRelativeId(ID_FUNCTION.apply(toMetaField), bean.getDefaultRecordId(), false, false)
);
}
// Возвращаем сформированный набор правил
return ruleSet;
}
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseBody
public RecodeRuleSetResponseBean createRecodeRuleSet(@Valid @RequestBody RecodeRuleSetRequestBean bean) {
// Выполняем подготовку набора правил
RecodeRuleSet ruleSet = convertRecodeRuleSet(null, bean);
// Выполняем создание и возвращаем результат
return RecodeRuleSetResponseBean.CONVERT.apply(
createSingleDocument(recodeRuleSetService, ruleSet, true)
);
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseBody
public RecodeRuleSetResponseBean updateRecodeRuleSet(@PathVariable String id, @Valid @RequestBody RecodeRuleSetRequestBean bean) {
// Выполняем подготовку набора правил
RecodeRuleSet ruleSet = convertRecodeRuleSet(id, bean);
// Выполняем обновление и возвращаем результат
return RecodeRuleSetResponseBean.CONVERT.apply(
updateSingleDocument(recodeRuleSetService, ruleSet, true)
);
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public void closeRecodeRuleSet(@PathVariable String id) {
recodeRuleSetService.closeByIDs(Sets.newHashSet(id));
}
}