All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.n2oapp.framework.engine.data.N2oInvocationProcessor Maven / Gradle / Ivy

The newest version!
package net.n2oapp.framework.engine.data;

import net.n2oapp.criteria.dataset.ArrayMergeStrategy;
import net.n2oapp.criteria.dataset.DataList;
import net.n2oapp.criteria.dataset.DataSet;
import net.n2oapp.criteria.dataset.FieldMapping;
import net.n2oapp.framework.api.MetadataEnvironment;
import net.n2oapp.framework.api.context.ContextProcessor;
import net.n2oapp.framework.api.data.ActionInvocationEngine;
import net.n2oapp.framework.api.data.ArgumentsInvocationEngine;
import net.n2oapp.framework.api.data.DomainProcessor;
import net.n2oapp.framework.api.data.InvocationProcessor;
import net.n2oapp.framework.api.exception.N2oException;
import net.n2oapp.framework.api.metadata.aware.MetadataEnvironmentAware;
import net.n2oapp.framework.api.metadata.compile.building.Placeholders;
import net.n2oapp.framework.api.metadata.global.dao.invocation.N2oArgumentsInvocation;
import net.n2oapp.framework.api.metadata.global.dao.invocation.N2oInvocation;
import net.n2oapp.framework.api.metadata.global.dao.object.AbstractParameter;
import net.n2oapp.framework.api.metadata.global.dao.object.field.ObjectListField;
import net.n2oapp.framework.api.metadata.global.dao.object.field.ObjectReferenceField;
import net.n2oapp.framework.api.metadata.global.dao.object.field.ObjectSetField;
import net.n2oapp.framework.api.metadata.global.dao.object.field.ObjectSimpleField;
import net.n2oapp.framework.engine.exception.N2oSpelException;
import net.n2oapp.framework.engine.util.MappingProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static net.n2oapp.framework.engine.util.ArgumentsInvocationUtil.mapToArgs;
import static net.n2oapp.framework.engine.util.MapInvocationUtil.mapToMap;
import static net.n2oapp.framework.engine.util.MappingProcessor.normalizeValue;
import static net.n2oapp.framework.engine.util.MappingProcessor.outMap;

/**
 * Процессор вызова процедур
 */
public class N2oInvocationProcessor implements InvocationProcessor, MetadataEnvironmentAware, ApplicationContextAware {
    private static final ExpressionParser parser = new SpelExpressionParser();

    private N2oInvocationFactory invocationFactory;
    private ContextProcessor contextProcessor;
    private DomainProcessor domainProcessor;
    private ApplicationContext applicationContext;

    public N2oInvocationProcessor(N2oInvocationFactory invocationFactory) {
        this.invocationFactory = invocationFactory;
    }

    @Override
    public DataSet invoke(N2oInvocation invocation, DataSet inDataSet,
                          Collection inParameters,
                          Collection outParameters) {
        final Map inMapping = MappingProcessor.extractFieldMapping(inParameters);
        final Map outMapping = MappingProcessor.extractFieldMapping(outParameters);
        prepareInValues(inParameters, inDataSet);
        DataSet resolvedInDataSet = resolveInValuesMapping(inParameters, inDataSet);
        DataSet resultDataSet = invoke(invocation, resolvedInDataSet, inMapping, outMapping);
        resolveOutValues(outParameters, resultDataSet, null);
        inDataSet.merge(resultDataSet, ArrayMergeStrategy.replace, true);
        return inDataSet;
    }

    private DataSet invoke(final N2oInvocation invocation, final DataSet inDataSet,
                           final Map inMapping,
                           final Map outMapping) {
        final ActionInvocationEngine engine = invocationFactory.produce(invocation.getClass());
        Object result;
        if (engine instanceof ArgumentsInvocationEngine)
            result = ((ArgumentsInvocationEngine) engine).invoke((N2oArgumentsInvocation) invocation,
                    mapToArgs((N2oArgumentsInvocation) invocation, inDataSet, inMapping, domainProcessor));
        else
            result = engine.invoke(invocation, mapToMap(inDataSet, inMapping));
        if (invocation.getResultMapping() != null)
            result = outMap(result, invocation.getResultMapping(), Object.class);
        if (invocation.getResultNormalize() != null)
            result = normalizeValue(result, invocation.getResultNormalize(), null, parser, applicationContext);

        return extractFields(result, outMapping);
    }

    /**
     * Преобразование исходящих данных вызова
     *
     * @param parameters    Исходящие поля операции
     * @param resultDataSet Исходящие данные вызова
     */
    protected void resolveOutValues(Collection parameters, DataSet resultDataSet, DataSet parentDataSet) {
        if (parameters == null) return;
        for (AbstractParameter parameter : parameters) {
            Object value = resultDataSet.get(parameter.getId());
            if (parameter instanceof ObjectReferenceField) {
                if (value != null) {
                    ObjectReferenceField referenceField = (ObjectReferenceField) parameter;
                    if (value instanceof Collection) {
                        for (Object obj : (Collection) value)
                            resolveOutValues(List.of(referenceField.getFields()), (DataSet) obj, resultDataSet);
                    } else if (value instanceof DataSet) {
                        resolveOutValues(List.of(referenceField.getFields()), (DataSet) value, resultDataSet);
                    }
                    if (parameter.getNormalize() != null) {
                        value = tryToNormalize(value, parameter, resultDataSet, parentDataSet, applicationContext);
                        resultDataSet.put(parameter.getId(), value);
                    }
                }
            } else {
                ObjectSimpleField simpleField = (ObjectSimpleField) parameter;
                resolveOutField(simpleField, resultDataSet, parentDataSet, value);
                if (simpleField.getN2oSwitch() != null)
                    applySwitch(resultDataSet, simpleField);
            }
        }
    }

    private void resolveOutField(ObjectSimpleField simpleField, DataSet resultDataSet, DataSet parentData, Object value) {
        if (value == null && simpleField.getDefaultValue() != null && simpleField.getMapping() == null)
            value = contextProcessor.resolve(simpleField.getDefaultValue());
        if (value != null && simpleField.getNormalize() != null)
            value = tryToNormalize(value, simpleField, resultDataSet, parentData, applicationContext);
        resultDataSet.put(simpleField.getId(), value);
    }

    /**
     * Преобразование входящих данных вызова с учетом маппинга
     *
     * @param invocationParameters Входящие поля операции
     * @param inDataSet            Входящие данные вызова
     * @return Преобразованные входящие данные вызова
     */
    protected DataSet resolveInValuesMapping(Collection invocationParameters,
                                             DataSet inDataSet) {

        if (invocationParameters == null || inDataSet == null)
            return new DataSet();
        return normalizeAndMapFields(invocationParameters, inDataSet, null);
    }

    private DataSet normalizeAndMapFields(Collection invocationParameters,
                                          DataSet inDataSet, DataSet parentData) {
        DataSet resultDataSet = new DataSet();
        // normalize values
        invocationParameters.forEach(parameter -> {
            Object value = inDataSet.get(parameter.getId());
            if (parameter.getNormalize() != null)
                value = tryToNormalize(value, parameter, inDataSet, parentData, applicationContext);
            resultDataSet.put(parameter.getId(), value);
        });
        // normalize children
        invocationParameters.stream()
                .filter(parameter -> parameter instanceof ObjectReferenceField &&
                        ((ObjectReferenceField) parameter).getFields() != null && resultDataSet.get(parameter.getId()) != null)
                .forEach(parameter -> normalizeInnerFields((ObjectReferenceField) parameter, resultDataSet));
        //switch
        invocationParameters.stream()
                .filter(parameter -> parameter instanceof ObjectSimpleField && ((ObjectSimpleField) parameter).getN2oSwitch() != null)
                .forEach(parameter -> applySwitch(resultDataSet, (ObjectSimpleField) parameter));
        // mapping
        invocationParameters.stream().filter(parameter -> parameter instanceof ObjectReferenceField &&
                        ((ObjectReferenceField) parameter).getFields() != null &&
                        ((ObjectReferenceField) parameter).getEntityClass() != null)
                .forEach(parameter -> MappingProcessor.mapParameter((ObjectReferenceField) parameter, resultDataSet));
        return resultDataSet;
    }

    private void applySwitch(DataSet resultDataSet, ObjectSimpleField parameter) {
        String valueFieldId = parameter.getN2oSwitch().getValueFieldId();
        String checkedValue = (String) resultDataSet.get(valueFieldId);
        String resolvedValue = parameter.getN2oSwitch().getResolvedCases().getOrDefault(checkedValue, parameter.getN2oSwitch().getDefaultCase());
        resultDataSet.put(valueFieldId, resolvedValue);
    }

    private void normalizeInnerFields(ObjectReferenceField field, DataSet dataSet) {
        List innerParams = Arrays.asList(field.getFields());
        if (field instanceof ObjectListField || field instanceof ObjectSetField) {
            DataList list = new DataList(dataSet.getList(field.getId()));
            for (int i = 0; i < list.size(); i++)
                list.set(i, normalizeAndMapFields(innerParams, (DataSet) dataSet.getList(field.getId()).get(i), dataSet));
            dataSet.put(field.getId(), list);
        } else {
            dataSet.put(field.getId(), normalizeAndMapFields(innerParams, dataSet.getDataSet(field.getId()), dataSet));
        }
    }

    /**
     * Установка значений во входящие поля вызова с учетом значения по умолчанию и
     * типа соответствующего поля операции
     *
     * @param parameters Входящее поле операции
     * @param inDataSet  Входящие данные вызова
     */
    private void prepareInValues(Collection parameters, DataSet inDataSet) {
        if (inDataSet == null)
            return;

        for (AbstractParameter parameter : parameters) {
            if (parameter instanceof ObjectSimpleField) {
                ObjectSimpleField simpleField = (ObjectSimpleField) parameter;
                Object value = inDataSet.get(simpleField.getId());
                if (value == null)
                    value = simpleField.getDefaultValue();
                value = contextProcessor.resolve(value);
                value = domainProcessor.deserialize(value, simpleField.getDomain());
                inDataSet.put(simpleField.getId(), value);
            } else if (parameter instanceof ObjectReferenceField) {
                Object value = inDataSet.get(parameter.getId());
                if (value == null)
                    continue;

                if (parameter.getClass().equals(ObjectReferenceField.class))
                    prepareComplexInValues(parameter, value, "");
                else {
                    // auto cast to collection if not collection
                    if (!(value instanceof Collection)) {
                        DataList list = new DataList();
                        list.add(value);
                        inDataSet.put(parameter.getId(), list);
                    }

                    for (Object dataSet : (Collection) inDataSet.get(parameter.getId()))
                        prepareComplexInValues(parameter, dataSet, "");
                }
            }
        }
    }

    private void prepareComplexInValues(AbstractParameter parameter, Object dataSet, String parameterType) {
        if (!(dataSet instanceof DataSet)) {
            throw new N2oException(
                    String.format("Значение поля '%s' не предназначено для использования с %s",
                            parameter.getId(), parameterType));
        }
        AbstractParameter[] fields = ((ObjectReferenceField) parameter).getFields();
        if (fields != null)
            prepareInValues(Arrays.asList(fields), (DataSet) dataSet);
    }

    private Object tryToNormalize(Object value,
                                  AbstractParameter parameter,
                                  DataSet inDataSet,
                                  DataSet parentData,
                                  ApplicationContext applicationContext) {
        try {
            value = normalizeValue(value, parameter.getNormalize(), inDataSet, parentData, parser, applicationContext);
        } catch (N2oSpelException e) {
            e.setFieldId(parameter.getId());
            throw e;
        }
        return value;
    }

    @Override
    public void setEnvironment(MetadataEnvironment environment) {
        this.contextProcessor = environment.getContextProcessor();
        this.domainProcessor = environment.getDomainProcessor();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * Построение набора данных по объекту с учетом маппинга полей
     *
     * @param source        Исходный объект
     * @param fieldsMapping Маппинг полей
     * @return Набор данных объекта
     */
    private DataSet extractFields(Object source, Map fieldsMapping) {
        DataSet result = new DataSet();
        for (Map.Entry map : fieldsMapping.entrySet()) {
            String fieldMapping = map.getValue().getMapping() != null
                    ? map.getValue().getMapping()
                    : Placeholders.spel(map.getKey());
            outMap(result, source, map.getKey(), fieldMapping, null, contextProcessor);
            if (map.getValue().getChildMapping() != null) {
                Object value = result.get(map.getKey());
                if (value instanceof Collection) {
                    DataList list = new DataList();
                    for (Object obj : (Collection) value)
                        list.add(extractFields(obj, map.getValue().getChildMapping()));
                    result.put(map.getKey(), list);
                } else if (value != null)
                    result.put(map.getKey(), extractFields(value, map.getValue().getChildMapping()));
            }
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy