package ru.hflabs.rcd.service.document.metaField;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import ru.hflabs.rcd.event.modify.ChangeEvent;
import ru.hflabs.rcd.exception.search.document.UnknownMetaFieldException;
import ru.hflabs.rcd.model.document.Dictionary;
import ru.hflabs.rcd.model.document.MetaField;
import ru.hflabs.rcd.model.path.DictionaryNamedPath;
import ru.hflabs.rcd.model.path.MetaFieldNamedPath;
import ru.hflabs.rcd.service.document.DocumentServiceTemplate;
import ru.hflabs.rcd.service.document.IDictionaryService;
import ru.hflabs.rcd.service.document.IMetaFieldService;
import ru.hflabs.util.core.EqualsUtil;
import ru.hflabs.util.core.Pair;
import ru.hflabs.util.spring.Assert;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static ru.hflabs.rcd.accessor.Accessors.linkRelative;
import static ru.hflabs.rcd.model.CriteriaUtils.*;
import static ru.hflabs.rcd.model.ModelUtils.*;
import static ru.hflabs.rcd.service.ServiceUtils.*;
/**
* Класс <class>MetaFieldService</class> реализует сервис работы с МЕТА-полями справочников
*
* @author Nazin Alexander
*/
public class MetaFieldService extends DocumentServiceTemplate<MetaField> implements IMetaFieldService {
/** Сервис работы со справочниками */
private IDictionaryService dictionaryService;
public MetaFieldService() {
super(MetaField.class);
}
public void setDictionaryService(IDictionaryService dictionaryService) {
this.dictionaryService = dictionaryService;
}
@Override
protected Collection<MetaField> injectTransitiveDependencies(Collection<MetaField> objects) {
return super.injectTransitiveDependencies(injectRelations(objects, dictionaryService));
}
@Override
public MetaField findUniqueByRelativeId(String relativeId, String name, boolean fillTransitive, boolean quietly) {
MetaField result = findUniqueDocumentBy(this, createCriteriaByRelative(MetaField.DICTIONARY_ID, relativeId, MetaField.NAME, name), fillTransitive);
if (result == null && !quietly) {
throw new UnknownMetaFieldException(name);
}
return result;
}
@Override
public Collection<MetaField> findAllByRelativeId(String relativeId, String searchQuery, boolean fillTransitive) {
Assert.isTrue(StringUtils.hasText(relativeId), "ID must not be NULL or EMPTY");
Collection<MetaField> metaFields = findAllByCriteria(createCriteriaByIDs(MetaField.DICTIONARY_ID, relativeId).injectSearch(searchQuery), fillTransitive);
return sortMetaFieldsByOrdinal(metaFields);
}
@Override
public MetaField findUniqueByNamedPath(MetaFieldNamedPath path, boolean quietly) {
return findMetaFieldByNamedPath(path, quietly);
}
@Override
public Collection<MetaField> findMetaFieldsByNamedPath(DictionaryNamedPath path) {
validateDictionaryNamedPath(path);
final Dictionary dictionary = dictionaryService.findUniqueByNamedPath(path, false);
Collection<MetaField> metaFields = injectHistory(findAllByRelativeId(dictionary.getId(), null, false));
return Lists.newArrayList(Collections2.transform(metaFields, new Function<MetaField, MetaField>() {
@Override
public MetaField apply(MetaField input) {
return linkRelative(dictionary, input);
}
}));
}
@Override
public MetaField findMetaFieldByNamedPath(MetaFieldNamedPath path, boolean quietly) {
return extractSingleDocument(
findMetaFieldByNamedPath(Sets.newHashSet(path), true).values(),
quietly ?
null :
Pair.valueOf(path.getFieldName(), UnknownMetaFieldException.class)
);
}
@Override
public Map<MetaFieldNamedPath, MetaField> findMetaFieldByNamedPath(Set<MetaFieldNamedPath> paths, boolean quietly) {
Assert.isTrue(!CollectionUtils.isEmpty(paths), "Collection must not be NULL or EMPTY");
// Выполняем сортировку путей по справочникам
Map<DictionaryNamedPath, Collection<MetaFieldNamedPath>> dictionary2path = Multimaps.index(paths, new Function<MetaFieldNamedPath, DictionaryNamedPath>() {
@Override
public DictionaryNamedPath apply(MetaFieldNamedPath input) {
validateMetaFieldNamedPath(input);
return new DictionaryNamedPath(input.getGroupName(), input.getDictionaryName());
}
}).asMap();
// Выполняем поиск МЕТА-полей
Map<MetaFieldNamedPath, MetaField> result = new HashMap<>(paths.size());
for (Map.Entry<DictionaryNamedPath, Collection<MetaFieldNamedPath>> entry : dictionary2path.entrySet()) {
Collection<MetaField> metaFields = findMetaFieldsByNamedPath(entry.getKey());
for (final MetaFieldNamedPath path : entry.getValue()) {
MetaField metaField = extractSingleDocument(Collections2.filter(metaFields, new Predicate<MetaField>() {
@Override
public boolean apply(MetaField input) {
return EqualsUtil.equals(input.getName(), path.getFieldName());
}
}), null);
if (metaField != null) {
result.put(path, metaField);
}
}
}
// Проверяем, что все МЕТА-поля найдены
if (!quietly && paths.size() != result.size()) {
Set<MetaFieldNamedPath> notFoundMetaFields = Sets.difference(paths, result.keySet());
throw new UnknownMetaFieldException(
StringUtils.collectionToCommaDelimitedString(notFoundMetaFields)
);
}
return result;
}
/**
* Выполняет выделение {@link MetaField#PRIMARY_KEY первичного ключа} из коллеции МЕТА-полей
*
* @param dictionary описание справочника
* @param metaFields коллекция МЕТА-полей
* @return Возвращает первичное МЕТА-поле
*/
private MetaField doFindPrimaryMetaField(String dictionary, Collection<MetaField> metaFields) {
MetaField primaryMetaField = retrievePrimaryMetaField(metaFields);
Assert.notNull(primaryMetaField, String.format("Can't find primary META field for dictionary '%s'", dictionary));
return primaryMetaField;
}
@Override
public MetaField findPrimaryMetaField(String dictionaryId, boolean fillTransitive, boolean quietly) {
Assert.isTrue(StringUtils.hasText(dictionaryId), "Dictionary id must not be NULL or EMPTY");
return doFindPrimaryMetaField(dictionaryId, findAllByRelativeId(dictionaryId, null, fillTransitive));
}
@Override
public MetaField findPrimaryMetaFieldByNamedPath(DictionaryNamedPath path, boolean quietly) {
validateDictionaryNamedPath(path);
return doFindPrimaryMetaField(path.toString(), findMetaFieldsByNamedPath(path));
}
@Override
protected void handleOtherCloseEvent(ChangeEvent event) {
if (Dictionary.class.equals(event.getChangedClass())) {
closeByCriteria(createCriteriaByDocumentIDs(MetaField.DICTIONARY_ID, event.getChanged(Dictionary.class)));
}
}
}