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

com.gs.dmn.DMNModelRepository Maven / Gradle / Ivy

There is a newer version: 8.7.3
Show newest version
/*
 * Copyright 2016 Goldman Sachs.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.gs.dmn;

import com.gs.dmn.ast.*;
import com.gs.dmn.ast.dmndi.DMNDI;
import com.gs.dmn.ast.dmndi.DMNDiagram;
import com.gs.dmn.ast.dmndi.DMNStyle;
import com.gs.dmn.ast.dmndi.DiagramElement;
import com.gs.dmn.runtime.DMNRuntimeException;
import com.gs.dmn.serialization.DMNVersion;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

import static com.gs.dmn.ast.TBuiltinAggregator.COUNT;
import static com.gs.dmn.ast.TBuiltinAggregator.SUM;

public class DMNModelRepository {
    public static final String FREE_TEXT_LANGUAGE = "free_text";
    protected static final ObjectFactory OBJECT_FACTORY = new ObjectFactory();

    protected static final Logger LOGGER = LoggerFactory.getLogger(DMNModelRepository.class);

    protected final List definitionsList;

    // Derived properties to optimise search
    protected final List allDefinitions = new ArrayList<>();
    protected final Map namespaceToDefinitions = new LinkedHashMap<>();
    protected final Map elementToDefinitions = new LinkedHashMap<>();
    protected List businessKnowledgeModels;
    protected List itemDefinitions;
    protected Map drgElementByName = new LinkedHashMap<>();
    protected Map knowledgeModelByName = new LinkedHashMap<>();
    protected Map drgElementByRef = new LinkedHashMap<>();
    protected Map> drgElementsByModel = new LinkedHashMap<>();
    protected Map> decisionsByModel = new LinkedHashMap<>();
    protected Map> inputDatasByModel = new LinkedHashMap<>();
    protected Map> bkmsByModel = new LinkedHashMap<>();
    protected Map> dssByModel = new LinkedHashMap<>();

    public DMNModelRepository() {
        this(OBJECT_FACTORY.createTDefinitions());
    }

    public DMNModelRepository(TDefinitions definitions) {
        this(Collections.singletonList(definitions));
    }

    public DMNModelRepository(List definitionsList) {
        this.definitionsList = definitionsList;
        // Normalize definitions
        this.definitionsList.sort(Comparator.comparing((TDefinitions o) -> NameUtils.removeSingleQuotes(o.getName())));
        if (definitionsList != null) {
            for (TDefinitions definitions: definitionsList) {
                this.allDefinitions.add(definitions);
                if (!this.namespaceToDefinitions.containsKey(definitions.getNamespace())) {
                    this.namespaceToDefinitions.put(definitions.getNamespace(), definitions);
                } else {
                    throw new DMNRuntimeException(String.format("Duplicated model namespace '%s'", definitions.getNamespace()));
                }
            }
        }

        // Process all definitions
        for (TDefinitions definitions: this.getAllDefinitions()) {
            // Normalize
            normalize(definitions);

            // Set derived properties
            for (TNamedElement element: findItemDefinitions(definitions)) {
                this.elementToDefinitions.put(element, definitions);
            }
            for (TDRGElement element: findDRGElements(definitions)) {
                this.elementToDefinitions.put(element, definitions);
            }
        }
    }

    public DMNModelRepository copy() {
        return new DMNModelRepository(this.definitionsList);
    }

    protected void normalize(TDefinitions definitions) {
        if (definitions != null) {
            sortDRGElements(definitions.getDrgElement());
            sortNamedElements(definitions.getItemDefinition());
            sortDMNDI(definitions.getDMNDI());
        }
    }

    public Set computeCachedElements(boolean cachingFlag, int cachingThreshold) {
        if (!cachingFlag) {
            return new LinkedHashSet<>();
        }

        LOGGER.info("Scanning for decisions to cache ...");

        Map map = new LinkedHashMap<>();
        Map parentMap = new LinkedHashMap<>();
        for (TDefinitions definitions: this.getAllDefinitions()) {
            for (TDecision decision: findDecisions(definitions)) {
                addCachedChildren(definitions, decision, parentMap, map);
            }
            for (TDecisionService service: findDSs(definitions)) {
                addCachedChildren(definitions, service, parentMap, map);
            }
        }

        Set result = new LinkedHashSet<>();
        for (Map.Entry entry: map.entrySet()) {
            if (entry.getValue() > cachingThreshold) {
                String key = entry.getKey();
                TDecision drgElement = this.findDecisionByRef(parentMap.get(key), key);
                if (drgElement != null) {
                    result.add(name(drgElement));
                }
            }
        }

        LOGGER.info("Decisions to be cached: {}", String.join(", ", result));

        return result;
    }

    private void addCachedChildren(TDefinitions definitions, TDecision decision, Map parentMap, Map map) {
        for (TInformationRequirement ir: decision.getInformationRequirement()) {
            TDMNElementReference requiredDecision = ir.getRequiredDecision();
            if (requiredDecision != null) {
                String href = requiredDecision.getHref();
                if (!hasNamespace(href)) {
                    href = makeRef(definitions.getNamespace(), href);
                }
                map.compute(href, (k, v) -> v == null ? 1 : v + 1);
                parentMap.put(href, decision);
            }
        }
    }

    private void addCachedChildren(TDefinitions definitions, TDecisionService decisionService, Map parentMap, Map map) {
        for (TDMNElementReference inputDecisionRef: decisionService.getInputDecision()) {
            if (inputDecisionRef != null) {
                String href = inputDecisionRef.getHref();
                if (!hasNamespace(href)) {
                    href = makeRef(definitions.getNamespace(), href);
                }
                map.compute(href, (k, v) -> v == null ? 1 : v + 1);
                TDecision inputDecision = findDecisionByRef(decisionService, href);
                parentMap.put(href, inputDecision);
            }
        }
    }

    public TDefinitions getRootDefinitions() {
        int size = this.getAllDefinitions().size();
        if (size == 1) {
            return this.allDefinitions.get(0);
        } else {
            throw new DMNRuntimeException(String.format("Cannot resolve root DM, there are '%d' DMs", size));
        }
    }

    public List getAllDefinitions() {
        return this.allDefinitions;
    }

    public void addElementMap(TDRGElement element, TDefinitions definitions) {
        this.elementToDefinitions.put(element, definitions);
    }

    public List getImportedNames() {
        List names = new ArrayList<>();
        for (TDefinitions definitions: this.allDefinitions) {
            for (TImport imp: definitions.getImport()) {
                names.add(imp.getName());
            }
        }
        return names;
    }

    public TDefinitions getModel(String namespace) {
        return this.namespaceToDefinitions.get(namespace);
    }

    public TDefinitions getModel(TNamedElement element) {
        return this.elementToDefinitions.get(element);
    }

    public String makeLocation(TDefinitions definitions, TDMNElement element) {
        if (definitions == null && element == null) {
            return null;
        }

        List locationParts = new ArrayList<>();
        addModelCoordinates(definitions, element, locationParts);
        addElementCoordinates(element, locationParts);
        return locationParts.isEmpty() ? null : String.format("(%s)", String.join(", ", locationParts));
    }

    protected void addModelCoordinates(TDefinitions definitions, TDMNElement element, List locationParts) {
        if (definitions != null) {
            String modelName = definitions.getName();
            if (!StringUtils.isBlank(modelName)) {
                locationParts.add(String.format("model='%s'", modelName));
            }
        }
    }

    protected void addElementCoordinates(TDMNElement element, List locationParts) {
        if (element != null) {
            String id = element.getId();
            String label = element.getLabel();
            String name = element instanceof TNamedElement ? ((TNamedElement) element).getName() : null;
            if (!StringUtils.isBlank(label)) {
                locationParts.add(String.format("label='%s'", label));
            }
            if (!StringUtils.isBlank(name)) {
                locationParts.add(String.format("name='%s'", name));
            }
            if (!StringUtils.isBlank(id)) {
                locationParts.add(String.format("id='%s'", id));
            }
        }
    }

    public String getNamespace(TNamedElement element) {
        TDefinitions definitions = this.elementToDefinitions.get(element);
        return definitions == null ? null : definitions.getNamespace();
    }

    public String getModelName(TNamedElement element) {
        TDefinitions definitions = this.elementToDefinitions.get(element);
        return definitions == null ? null : definitions.getName();
    }

    private List findAllBKMs() {
        if (this.businessKnowledgeModels == null) {
            this.businessKnowledgeModels = new ArrayList<>();
            for (TDefinitions definitions: this.allDefinitions) {
                collectBKMs(definitions, this.businessKnowledgeModels);
            }
        }
        return this.businessKnowledgeModels;
    }

    private List findAllItemDefinitions() {
        if (this.itemDefinitions == null) {
            this.itemDefinitions = new ArrayList<>();
            for (TDefinitions definitions: this.allDefinitions) {
                this.itemDefinitions.addAll(definitions.getItemDefinition());
            }
        }
        return this.itemDefinitions;
    }

    public List findDRGElements(TDefinitions definitions) {
        List result = this.drgElementsByModel.get(definitions);
        if (result == null) {
            result = new ArrayList<>();
            collectDRGElements(definitions, result);
            this.drgElementsByModel.put(definitions, result);
        }
        return result;
    }

    public List findDecisions(TDefinitions definitions) {
        List result = this.decisionsByModel.get(definitions);
        if (result == null) {
            result = new ArrayList<>();
            collectDecisions(definitions, result);
            this.decisionsByModel.put(definitions, result);
        }
        return result;
    }

    public List findInputDatas(TDefinitions definitions) {
        List result = this.inputDatasByModel.get(definitions);
        if (result == null) {
            result = new ArrayList<>();
            collectInputDatas(definitions, result);
            this.inputDatasByModel.put(definitions, result);
        }
        return result;
    }

    public List findBKMs(TDefinitions definitions) {
        List result = this.bkmsByModel.get(definitions);
        if (result == null) {
            result = new ArrayList<>();
            collectBKMs(definitions, result);
            this.bkmsByModel.put(definitions, result);
        }
        return result;
    }

    public List findDSs(TDefinitions definitions) {
        List result = this.dssByModel.get(definitions);
        if (result == null) {
            result = new ArrayList<>();
            collectDSs(definitions, result);
            this.dssByModel.put(definitions, result);
        }
        return result;
    }

    public List findItemDefinitions(TDefinitions definitions) {
        return definitions.getItemDefinition();
    }

    protected void collectDRGElements(TDefinitions definitions, List result) {
        for (TDRGElement element: definitions.getDrgElement()) {
            result.add(element);
        }
    }

    protected void collectDecisions(TDefinitions definitions, List result) {
        for (TDRGElement element: definitions.getDrgElement()) {
            if (element instanceof TDecision) {
                result.add((TDecision) element);
            }
        }
    }

    protected void collectInputDatas(TDefinitions definitions, List result) {
        for (TDRGElement element: definitions.getDrgElement()) {
            if (element instanceof TInputData) {
                result.add((TInputData) element);
            }
        }
    }

    protected void collectBKMs(TDefinitions definitions, List result) {
        for (TDRGElement element: definitions.getDrgElement()) {
            if (element instanceof TBusinessKnowledgeModel) {
                result.add((TBusinessKnowledgeModel) element);
            }
        }
    }

    protected void collectDSs(TDefinitions definitions, List result) {
        for (TDRGElement element: definitions.getDrgElement()) {
            if (element instanceof TDecisionService) {
                result.add((TDecisionService) element);
            }
        }
    }

    public boolean hasComponents(TItemDefinition itemDefinition) {
        return !this.isEmpty(itemDefinition.getItemComponent());
    }

    public List sortItemComponent(TItemDefinition itemDefinition) {
        if (itemDefinition == null || itemDefinition.getItemComponent() == null) {
            return new ArrayList<>();
        }
        List children = new ArrayList<>(itemDefinition.getItemComponent());
        children.sort((o1, o2) -> {
            if (o1 == null && o2 == null) {
                return 0;
            } else if (o1 == null) {
                return 1;
            } else if (o2 == null) {
                return -1;
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        });
        return children;
    }

    public TItemDefinition normalize(TItemDefinition itemDefinition) {
        while (true) {
            TItemDefinition next = next(itemDefinition);
            if (next != null) {
                itemDefinition = next;
            } else {
                break;
            }
        }
        return itemDefinition;
    }

    protected TItemDefinition next(TItemDefinition itemDefinition) {
        if (itemDefinition == null
                || itemDefinition.isIsCollection()
                || !isEmpty(itemDefinition.getItemComponent())
                || itemDefinition.getFunctionItem() != null
                || itemDefinition.getTypeRef() == null) {
            return null;
        }
        TDefinitions model = getModel(itemDefinition);
        return lookupItemDefinition(model, QualifiedName.toQualifiedName(model, itemDefinition.getTypeRef()));
    }

    protected void sortDRGElements(List elements) {
        elements.sort(Comparator.comparing((TDRGElement o) -> NameUtils.removeSingleQuotes(o.getName())));
    }

    public void sortNamedElements(List elements) {
        elements.sort(Comparator.comparing((TNamedElement o) -> NameUtils.removeSingleQuotes(o.getName())));
    }

    private void sortDMNDI(DMNDI dmndi) {
        if (dmndi != null) {
            sortDMNDiagrams(dmndi);
            dmndi.getDMNStyle().sort(Comparator.comparing((DMNStyle s) -> styleKey(s)));
        }
    }

    private String styleKey(DMNStyle s) {
        if (s == null) {
            return "";
        }
        return s.getId();
    }

    private void sortDMNDiagrams(DMNDI dmndi) {
        List diagrams = dmndi.getDMNDiagram();
        diagrams.sort(Comparator.comparing((DMNDiagram d) -> diagramKey(d)));
        for (DMNDiagram d: diagrams) {
            d.getDMNDiagramElement().sort(Comparator.comparing((DiagramElement e) -> diagramElementKey(e)));
        }
    }

    private String diagramKey(DMNDiagram d) {
        if (d == null) {
            return "";
        }
        return String.format("%s-%s", d.getName(), d.getId());
    }

    private String diagramElementKey(DiagramElement e) {
        if (e == null) {
            return "";
        }
        return String.format("%s-%s", e.getClass().getSimpleName(), e.getId());
    }

    public void sortNamedElementReferences(List> references) {
        references.sort(Comparator.comparing((DRGElementReference o) -> NameUtils.removeSingleQuotes(o.getElementName())));
    }

    public TDRGElement findDRGElementByRef(TDRGElement parent, String href) {
        try {
            TDefinitions definitions = findChildDefinitions(parent, href);
            if (definitions == null) {
                throw new DMNRuntimeException(String.format("Cannot find model for href='%s'", href));
            }
            String key = makeRef(definitions.getNamespace(), href);
            TDRGElement result = this.drgElementByRef.get(key);
            if (result == null) {
                for (TDRGElement element: findDRGElements(definitions)) {
                    if (sameId(element, href)) {
                        result = element;
                        this.drgElementByRef.put(key, result);
                        break;
                    }
                }
            }
            if (result == null) {
                throw new DMNRuntimeException(String.format("Cannot find DRG element for href='%s'", href));
            } else {
                return result;
            }
        } catch (Exception e) {
            throw new DMNRuntimeException(String.format("Cannot find DRG element for href='%s'", href), e);
        }
    }

    public TDecision findDecisionByRef(TDRGElement parent, String href) {
        TDRGElement drgElement = findDRGElementByRef(parent, href);
        if (drgElement instanceof TDecision) {
            return (TDecision) drgElement;
        } else {
            throw new DMNRuntimeException(String.format("Cannot find Decision element for href='%s'", href));
        }
    }

    public TInputData findInputDataByRef(TDRGElement parent, String href) {
        TDRGElement drgElement = findDRGElementByRef(parent, href);
        if (drgElement instanceof TInputData) {
            return (TInputData) drgElement;
        } else {
            throw new DMNRuntimeException(String.format("Cannot find InputData element for href='%s'", href));
        }
    }

    public TInvocable findInvocableByRef(TDRGElement parent, String href) {
        TDRGElement drgElement = findDRGElementByRef(parent, href);
        if (drgElement instanceof TInvocable) {
            return (TInvocable) drgElement;
        } else {
            throw new DMNRuntimeException(String.format("Cannot find TInvocable element for href='%s'", href));
        }
    }

    protected TDefinitions findChildDefinitions(TDRGElement parent, String href) {
        String namespace = extractNamespace(href);
        if (StringUtils.isEmpty(namespace)) {
            return this.elementToDefinitions.get(parent);
        } else {
            return this.namespaceToDefinitions.get(namespace);
        }
    }

    public TBusinessKnowledgeModel findKnowledgeModelByName(String name) {
        TBusinessKnowledgeModel result = this.knowledgeModelByName.get(name);
        if (result == null) {
            List value = new ArrayList<>();
            for (TBusinessKnowledgeModel knowledgeModel: findAllBKMs()) {
                if (sameName(knowledgeModel, name)) {
                    value.add(knowledgeModel);
                }
            }
            if (value.size() == 1) {
                result = value.get(0);
                this.knowledgeModelByName.put(name, result);
            } else if (value.size() > 1) {
                throw new DMNRuntimeException(String.format("Found %s business knowledge models for name='%s'", value.size(), name));
            }
        }
        if (result == null) {
            throw new DMNRuntimeException(String.format("Cannot find business knowledge model for name='%s'", name));
        } else {
            return result;
        }
    }

    public TDRGElement findDRGElementByName(String namespace, String name) {
        TDefinitions definitions = this.namespaceToDefinitions.get(namespace);
        if (definitions == null) {
            throw new DMNRuntimeException(String.format("Cannot find model for namespace '%s'", namespace));
        }
        return findDRGElementByName(definitions, name);
    }

    public TDRGElement findDRGElementByName(TDefinitions definitions, String name) {
        if (definitions == null) {
            throw new DMNRuntimeException(String.format("Cannot find element for name='%s'. Missing DM", name));
        }
        for (TDRGElement element: findDRGElements(definitions)) {
            if (element.getName().equals(name)) {
                return element;
            }
        }
        throw new DMNRuntimeException(String.format("Cannot find element for name='%s'", name));
    }

    public TDRGElement findDRGElementByName(String name) {
        TDRGElement result = this.drgElementByName.get(name);
        if (result == null) {
            List value = new ArrayList<>();
            for (TDefinitions definitions: getAllDefinitions()) {
                for (TDRGElement element: findDRGElements(definitions)) {
                    if (sameName(element, name)) {
                        value.add(element);
                    }
                }
            }
            if (value.size() == 1) {
                result = value.get(0);
                this.drgElementByName.put(name, result);
            } else if (value.size() > 1) {
                throw new DMNRuntimeException(String.format("Found %s business knowledge models for name='%s'", value.size(), name));
            }
        }
        if (result == null) {
            throw new DMNRuntimeException(String.format("Cannot find element for name='%s'", name));
        } else {
            return result;
        }
    }

    public boolean sameId(TDMNElement element, String href) {
        String id = extractId(href);
        return element.getId().equals(id);
    }

    public boolean sameName(TNamedElement element, String name) {
        return element.getName().equals(name);
    }

    public  DRGElementReference makeDRGElementReference(T element) {
        return makeDRGElementReference(new ImportPath(), element);
    }

    public  DRGElementReference makeDRGElementReference(String importName, T element) {
        return makeDRGElementReference(new ImportPath(importName), element);
    }

    public  DRGElementReference makeDRGElementReference(ImportPath importPath, T element) {
        return new DRGElementReference<>(importPath, getNamespace(element), getModelName(element), element);
    }

    public List> directInputDatas(TDRGElement parent) {
        List> result = new ArrayList<>();
        // Add reference for direct children
        List references = requiredInputDataReferences(parent);
        for (TDMNElementReference reference: references) {
            TInputData child = findInputDataByRef(parent, reference.getHref());
            if (child != null) {
                String importName = findImportName(parent, reference);
                result.add(makeDRGElementReference(importName, child));
            } else {
                throw new DMNRuntimeException(String.format("Cannot find InputData for '%s' in parent '%s'", reference.getHref(), parent.getName()));
            }
        }
        return result;
    }

    public List> inputDataClosure(DRGElementReference parentReference, DRGElementFilter drgElementFilter) {
        return drgElementFilter.filterInputs(collectTransitiveInputDatas(parentReference));
    }

    protected List> collectTransitiveInputDatas(DRGElementReference parentReference) {
        TDRGElement parent = parentReference.getElement();
        ImportPath parentImportPath = parentReference.getImportPath();
        List> result = new ArrayList<>();
        // Add reference for direct children
        List references = requiredInputDataReferences(parent);
        for (TDMNElementReference reference: references) {
            TInputData child = findInputDataByRef(parent, reference.getHref());
            if (child != null) {
                String importName = findImportName(parent, reference);
                result.add(makeDRGElementReference(new ImportPath(parentImportPath, importName), child));
            } else {
                throw new DMNRuntimeException(String.format("Cannot find InputData for '%s' in parent '%s'", reference.getHref(), parent.getName()));
            }
        }

        // Process direct children and update reference
        List childReferences = requiredDecisionReferences(parent);
        for (TDMNElementReference reference: childReferences) {
            TDecision child = findDecisionByRef(parent, reference.getHref());
            if (child != null) {
                // Update reference for descendants
                String importName = findImportName(parent, reference);
                List> inputReferences = collectTransitiveInputDatas(makeDRGElementReference(new ImportPath(parentImportPath, importName), child));
                result.addAll(inputReferences);
            } else {
                throw new DMNRuntimeException(String.format("Cannot find Decision for '%s' in parent '%s'", reference.getHref(), parent.getName()));
            }
        }
        return result;
    }

    public List> directSubDecisions(TDRGElement parent) {
        List> result = new ArrayList<>();
        if (parent instanceof TDecision) {
            // Add reference for direct children
            for (TInformationRequirement ir: ((TDecision) parent).getInformationRequirement()) {
                TDMNElementReference reference = ir.getRequiredDecision();
                if (reference != null) {
                    TDecision child = findDecisionByRef(parent, reference.getHref());
                    String importName = findImportName(parent, reference);
                    result.add(makeDRGElementReference(importName, child));
                }
            }
        } else if (parent instanceof TDecisionService) {
            // Add reference for direct children
            for (TDMNElementReference outputDecisionRef: ((TDecisionService) parent).getOutputDecision()) {
                TDecision child = findDecisionByRef(parent, outputDecisionRef.getHref());
                String importName = findImportName(parent, outputDecisionRef);
                result.add(makeDRGElementReference(importName, child));
            }
        }
        sortNamedElementReferences(result);
        return result;
    }

    public List> directInputDecisions(TDecisionService parent) {
        List> result = new ArrayList<>();
        // Add reference for direct children
        for (TDMNElementReference inputDecisionRef: parent.getInputDecision()) {
            TDecision child = findDecisionByRef(parent, inputDecisionRef.getHref());
            String importName = findImportName(parent, inputDecisionRef);
            result.add(makeDRGElementReference(importName, child));
        }
        sortNamedElementReferences(result);
        return result;
    }

    public List> directSubInvocables(TDRGElement element) {
        List> result = new ArrayList<>();
        // Add reference for direct children
        List knowledgeRequirements = knowledgeRequirements(element);
        for (TKnowledgeRequirement kr: knowledgeRequirements) {
            TDMNElementReference reference = kr.getRequiredKnowledge();
            if (reference != null) {
                TInvocable invocable = findInvocableByRef(element, reference.getHref());
                if (invocable != null) {
                    String importName = findImportName(element, reference);
                    result.add(makeDRGElementReference(importName, invocable));
                } else {
                    throw new DMNRuntimeException(String.format("Cannot find Invocable for '%s'", reference.getHref()));
                }
            }
        }
        return result;
    }

    public List> directDRGElements(TDRGElement element) {
        List> result = new ArrayList<>();
        result.addAll(directInputDatas(element));
        result.addAll(directSubDecisions(element));
        result.addAll(directSubInvocables(element));
        return result;
    }

    protected List requiredInputDataReferences(TDRGElement parent) {
        List references = new ArrayList<>();
        if (parent instanceof TDecision) {
            List informationRequirements = ((TDecision) parent).getInformationRequirement();
            references.addAll(informationRequirements.stream().map(TInformationRequirement::getRequiredInput).filter(Objects::nonNull).collect(Collectors.toList()));
        } else if (parent instanceof TDecisionService) {
            List inputData = ((TDecisionService) parent).getInputData();
            references.addAll(inputData.stream().filter(Objects::nonNull).collect(Collectors.toList()));
        }
        return references;
    }

    protected List requiredDecisionReferences(TDRGElement parent) {
        List references = new ArrayList<>();
        if (parent instanceof TDecision) {
            List informationRequirements = ((TDecision) parent).getInformationRequirement();
            references.addAll(informationRequirements.stream().map(TInformationRequirement::getRequiredDecision).filter(Objects::nonNull).collect(Collectors.toList()));
        } else if (parent instanceof TDecisionService) {
            List inputDecisions = ((TDecisionService) parent).getInputDecision();
            references.addAll(inputDecisions.stream().filter(Objects::nonNull).collect(Collectors.toList()));
            List outputDecisions = ((TDecisionService) parent).getOutputDecision();
            references.addAll(outputDecisions.stream().filter(Objects::nonNull).collect(Collectors.toList()));
            List encapsulatedDecision = ((TDecisionService) parent).getEncapsulatedDecision();
            references.addAll(encapsulatedDecision.stream().filter(Objects::nonNull).collect(Collectors.toList()));
        }
        return references;
    }

    protected List knowledgeRequirements(TDRGElement element) {
        List knowledgeRequirements;
        if (element instanceof TDecision) {
            knowledgeRequirements = ((TDecision) element).getKnowledgeRequirement();
        } else if (element instanceof TBusinessKnowledgeModel) {
            knowledgeRequirements = ((TBusinessKnowledgeModel) element).getKnowledgeRequirement();
        } else {
            knowledgeRequirements = new ArrayList<>();
        }
        return knowledgeRequirements;
    }

    public TDecisionTable decisionTable(TDRGElement element) {
        TExpression expression = expression(element);
        if (expression instanceof TDecisionTable) {
            return (TDecisionTable) expression;
        } else {
            throw new DMNRuntimeException(String.format("Cannot find decision table in element '%s'", element.getName()));
        }
    }

    public boolean isNull(String typeRef) {
        return typeRef == null;
    }

    public boolean isNull(QualifiedName typeRef) {
        return typeRef == null;
    }

    public boolean isAny(QualifiedName typeRef) {
        return typeRef != null && "Any".equals(typeRef.getLocalPart());
    }

    public boolean isNullOrAny(QualifiedName typeRef) {
        return isNull(typeRef) || isAny(typeRef);
    }

    public TItemDefinition lookupItemDefinition(TDefinitions model, QualifiedName typeRef) {
        if (isNull(typeRef)) {
            return null;
        }
        String importName = typeRef.getNamespace();
        if (DMNVersion.LATEST.getFeelPrefix().equals(importName)) {
            return null;
        }

        if (model == null) {
            return lookupItemDefinition(findAllItemDefinitions(), typeRef);
        } else {
            for (TImport import_: model.getImport()) {
                if (import_.getName().equals(importName)) {
                    String modelNamespace = import_.getNamespace();
                    model = this.getModel(modelNamespace);
                    if (model == null) {
                        throw new DMNRuntimeException(String.format("Cannot find DM for '%s'", modelNamespace));
                    }
                }
            }
            return lookupItemDefinition(findItemDefinitions(model), typeRef);
        }
    }

    protected TItemDefinition lookupItemDefinition(List itemDefinitionList, QualifiedName typeRef) {
        for (TItemDefinition itemDefinition: itemDefinitionList) {
            if (typeRef.getLocalPart().equals(itemDefinition.getName())) {
                return itemDefinition;
            }
        }
        return null;
    }

    public TItemDefinition lookupItemDefinition(String name) {
        return lookupItemDefinition(findAllItemDefinitions(), name);
    }

    protected TItemDefinition lookupItemDefinition(List itemDefinitionList, String name) {
        if (name == null) {
            return null;
        }
        for (TItemDefinition itemDefinition: itemDefinitionList) {
            if (name.equals(itemDefinition.getName())) {
                return itemDefinition;
            }
        }
        return null;
    }

    public List> sortedUniqueInputs(TDecision decision, DRGElementFilter drgElementFilter) {
        List> inputs = new ArrayList<>();
        inputs.addAll(directInputDatas(decision));
        inputs.addAll(directSubDecisions(decision));
        List> result = drgElementFilter.filterDRGElements(inputs);
        sortNamedElementReferences(result);
        return result;
    }

    public boolean hasDefaultValue(TDecisionTable decisionTable) {
        List outputClauses = decisionTable.getOutput();
        for (TOutputClause output: outputClauses) {
            TLiteralExpression defaultOutputEntry = output.getDefaultOutputEntry();
            if (defaultOutputEntry != null) {
                return true;
            }
        }
        return false;
    }

    public TExpression expression(TDRGElement element) {
        if (element instanceof TDecision) {
            TExpression expression = ((TDecision) element).getExpression();
            return expression;
        } else if (element instanceof TBusinessKnowledgeModel) {
            TFunctionDefinition encapsulatedLogic = ((TBusinessKnowledgeModel) element).getEncapsulatedLogic();
            if (encapsulatedLogic != null) {
                TExpression expression = encapsulatedLogic.getExpression();
                return expression;
            }
        } else if (element instanceof TDecisionService) {
            return null;
        } else {
            throw new UnsupportedOperationException(String.format("'%s' is not supported yet", element.getClass().getSimpleName()));
        }
        return null;
    }

    public boolean isLiteralExpression(TDRGElement element) {
        return expression(element) instanceof TLiteralExpression;
    }

    public boolean isFreeTextLiteralExpression(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TLiteralExpression
                && FREE_TEXT_LANGUAGE.equals(((TLiteralExpression) expression).getExpressionLanguage());
    }

    public boolean isDecisionTableExpression(TDRGElement element) {
        return expression(element) instanceof TDecisionTable;
    }

    public boolean isCompoundDecisionTable(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TDecisionTable
                && ((TDecisionTable) expression).getOutput() != null
                && ((TDecisionTable) expression).getOutput().size() > 1;
    }

    public boolean isInvocationExpression(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TInvocation;
    }

    public boolean isContextExpression(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TContext;
    }

    public boolean isRelationExpression(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TRelation;
    }

    public boolean isFunctionDefinitionExpression(TDRGElement element) {
        TExpression expression = expression(element);
        return expression instanceof TFunctionDefinition;
    }

    //
    // Item definition related functions
    //
    public boolean isEmpty(List list) {
        return list == null || list.isEmpty();
    }

    public QualifiedName variableTypeRef(TDefinitions model, TInformationItem element) {
        TInformationItem variable = variable(element);
        return variable == null ? null : QualifiedName.toQualifiedName(model, variable.getTypeRef());
    }

    public QualifiedName variableTypeRef(TDefinitions model, TDRGElement element) {
        TInformationItem variable = variable(element);
        QualifiedName typeRef = variable == null ? null : QualifiedName.toQualifiedName(model, variable.getTypeRef());
        // Derive from expression
        if (isNull(typeRef) && element instanceof TDecision) {
            typeRef = inferExpressionTypeRef(model, element);
        }
        return typeRef;
    }

    public QualifiedName outputTypeRef(TDefinitions model, TDRGElement element) {
        // Derive from variable
        TInformationItem variable = variable(element);
        QualifiedName typeRef = variable == null ? null : QualifiedName.toQualifiedName(model, variable.getTypeRef());
        // Derive from expression
        if (isNull(typeRef)) {
            typeRef = inferExpressionTypeRef(model, element);
        }
        return typeRef;
    }

    public QualifiedName inferExpressionTypeRef(TDefinitions model, TDRGElement element) {
        QualifiedName typeRef = null;
        // Derive from expression
        TExpression expression = expression(element);
        if (expression != null) {
            typeRef = QualifiedName.toQualifiedName(model, expression.getTypeRef());
            if (isNull(typeRef)) {
                if (expression instanceof TContext) {
                    // Derive from return entry
                    List contextEntryList = ((TContext) expression).getContextEntry();
                    for (TContextEntry ce: contextEntryList) {
                        if (ce.getVariable() == null) {
                            TExpression returnExp = ce.getExpression();
                            if (returnExp != null) {
                                typeRef = QualifiedName.toQualifiedName(model, returnExp.getTypeRef());
                            }
                        }
                    }
                } else if (expression instanceof TDecisionTable) {
                    // Derive from output clauses and rules
                    TDecisionTable dt = (TDecisionTable) expression;
                    List outputList = dt.getOutput();
                    if (outputList.size() == 1) {
                        typeRef = QualifiedName.toQualifiedName(model, outputList.get(0).getTypeRef());
                        if (isNull(typeRef)) {
                            // Derive from rules
                            List ruleList = dt.getRule();
                            List outputEntry = ruleList.get(0).getOutputEntry();
                            typeRef = QualifiedName.toQualifiedName(model, outputEntry.get(0).getTypeRef());
                        }
                        // Apply aggregation and hit policy
                        if (dt.getHitPolicy() == THitPolicy.COLLECT) {
                            // Type is list
                            typeRef = null;
                        }
                        TBuiltinAggregator aggregation = dt.getAggregation();
                        if (aggregation == SUM || aggregation == COUNT) {
                            typeRef = QualifiedName.toQualifiedName(null, "number");
                        }
                    }
                }
            }
        }
        return typeRef;
    }

    //
    // Decision Service related functions
    //
    public TDecision getOutputDecision(TDecisionService decisionService) {
        List outputDecisionList = decisionService.getOutputDecision();
        if (outputDecisionList.size() != 1) {
            throw new DMNRuntimeException(String.format("Missing or more than one decision services in BKM '%s'", decisionService.getName()));
        }
        return this.findDecisionByRef(decisionService, outputDecisionList.get(0).getHref());
    }

    //
    // DecisionTable related functions
    //
    public boolean isSingleHit(THitPolicy policy) {
        return policy == THitPolicy.FIRST
                || policy == THitPolicy.UNIQUE
                || policy == THitPolicy.ANY
                || policy == THitPolicy.PRIORITY
                || policy == null;
    }

    public boolean isFirstSingleHit(THitPolicy policy) {
        return policy == THitPolicy.FIRST;
    }

    public boolean atLeastTwoRules(TDecisionTable expression) {
        return expression.getRule() != null && expression.getRule().size() >= 2;
    }

    public boolean isMultipleHit(THitPolicy hitPolicy) {
        return THitPolicy.COLLECT == hitPolicy
                || THitPolicy.RULE_ORDER == hitPolicy
                || THitPolicy.OUTPUT_ORDER == hitPolicy
                ;
    }

    public boolean isOutputOrderHit(THitPolicy hitPolicy) {
        return THitPolicy.PRIORITY == hitPolicy
                || THitPolicy.OUTPUT_ORDER == hitPolicy
                ;
    }

    public boolean hasAggregator(TDecisionTable decisionTable) {
        return decisionTable.getAggregation() != null;
    }

    public int rulesCount(TDRGElement element) {
        TExpression expression = expression(element);
        if (expression instanceof TDecisionTable) {
            List rules = ((TDecisionTable) expression).getRule();
            return rules == null ? 0 : rules.size();
        } else {
            return -1;
        }
    }

    public String outputClauseName(TDRGElement element, TOutputClause output) {
        // Try TOutputClause.typeRef
        String outputClauseName = output.getName();
        if (StringUtils.isBlank(outputClauseName)) {
            // Try variable.typeRef from parent
            if (!isCompoundDecisionTable(element)) {
                TInformationItem variable = variable(element);
                if (variable != null) {
                    outputClauseName = variable.getName();
                }
            }
            if (StringUtils.isBlank(outputClauseName)) {
                throw new DMNRuntimeException(String.format("Cannot resolve name for outputClause '%s' in element '%s'", output.getId(), element.getName()));
            }
        }
        return outputClauseName;
    }

    public TInformationItem variable(TNamedElement element) {
        if (element instanceof TInputData) {
            return ((TInputData) element).getVariable();
        } else if (element instanceof TDecision) {
            return ((TDecision) element).getVariable();
        } else if (element instanceof TInvocable) {
            return ((TInvocable) element).getVariable();
        } else if (element instanceof TInformationItem) {
            return (TInformationItem) element;
        }
        return null;
    }

    public String name(TNamedElement element) {
        String name = null;
        if (element != null) {
            name = element.getName();
        }
        if (StringUtils.isBlank(name)) {
            throw new DMNRuntimeException(String.format("Display name cannot be null for element '%s'", element == null ? null : element.getId()));
        }
        return name.trim();
    }

    public String label(TDMNElement element) {
        String label = element.getLabel();
        return label == null ? "" : label.replace('\"', '\'');
    }

    public String displayName(TNamedElement element) {
        String name = label(element);
        if (StringUtils.isBlank(name)) {
            name = element.getName();
        }
        if (StringUtils.isBlank(name)) {
            throw new DMNRuntimeException(String.format("Display name cannot be null for element '%s'", element.getId()));
        }
        return name.trim();
    }

    public String registryId(TDRGElement element) {
        if (this.allDefinitions.size() == 1) {
            return displayName(element);
        } else {
            TDefinitions model = this.getModel(element);
            String modelId = model.getNamespace();
            return String.format("%s#%s", modelId, displayName(element));
        }
    }

    public String findChildImportName(TDRGElement parent, TDRGElement child) {
        // Collect references
        List references = new ArrayList<>();
        if (parent instanceof TDecision) {
            for (TInformationRequirement ir: ((TDecision) parent).getInformationRequirement()) {
                TDMNElementReference reference = ir.getRequiredDecision();
                if (reference != null) {
                    references.add(reference);
                }
                reference = ir.getRequiredInput();
                if (reference != null) {
                    references.add(reference);
                }
            }
            for (TKnowledgeRequirement bkr: ((TDecision) parent).getKnowledgeRequirement()) {
                TDMNElementReference reference = bkr.getRequiredKnowledge();
                if (reference != null) {
                    references.add(reference);
                }
            }
        } else if (parent instanceof TBusinessKnowledgeModel) {
            for (TKnowledgeRequirement bkr: ((TBusinessKnowledgeModel) parent).getKnowledgeRequirement()) {
                TDMNElementReference reference = bkr.getRequiredKnowledge();
                if (reference != null) {
                    references.add(reference);
                }
            }
        } else if (parent instanceof TDecisionService) {
            references.addAll(((TDecisionService) parent).getInputData());
            references.addAll(((TDecisionService) parent).getInputDecision());
            references.addAll(((TDecisionService) parent).getOutputDecision());
            references.addAll(((TDecisionService) parent).getEncapsulatedDecision());
        }

        // Find reference for child
        String childNamespace = getNamespace(child);
        String childRefSuffix = childNamespace + "#" + child.getId();
        for (TDMNElementReference reference: references) {
            if (reference.getHref().endsWith(childRefSuffix)) {
                return findImportName(parent, reference);
            }
        }
        return null;
    }

    public String findImportName(TDRGElement parent, TDMNElementReference reference) {
        TDefinitions parentDefinitions = this.elementToDefinitions.get(parent);
        String referenceNamespace = extractNamespace(reference.getHref());
        if (referenceNamespace == null) {
            return null;
        } else if (parentDefinitions.getNamespace().equals(referenceNamespace)) {
            return null;
        } else {
            for (TImport import_: parentDefinitions.getImport()) {
                if (import_.getNamespace().equals(referenceNamespace)) {
                    return import_.getName();
                }
            }
            throw new DMNRuntimeException(String.format("Cannot find import prefix for '%s' in model '%s'", reference.getHref(), parentDefinitions.getName()));
        }
    }

    protected static String makeRef(String namespace, String href) {
        if (StringUtils.isEmpty(namespace)) {
            return href;
        } else {
            return namespace + href;
        }
    }

    protected String extractNamespace(String href) {
        String namespace = null;
        if (hasNamespace(href)) {
            namespace = href.substring(0, href.indexOf('#'));
        }
        return namespace;
    }

    public static String extractId(String href) {
        if (hasNamespace(href)) {
            href = href.substring(href.indexOf('#') + 1);
        } else if (href != null && href.startsWith("#")) {
            href = href.substring(1);
        }
        return href;
    }

    protected static boolean hasNamespace(String href) {
        return href != null && href.indexOf('#') > 0;
    }

    public List compositeItemDefinitions(TDefinitions definitions) {
        List accumulator = new ArrayList<>();
        collectCompositeItemDefinitions(definitions.getItemDefinition(), accumulator);
        return accumulator;
    }

    private void collectCompositeItemDefinitions(List itemDefinitions, List accumulator) {
        if (itemDefinitions != null) {
            for (TItemDefinition itemDefinition: itemDefinitions) {
                if (hasComponents(itemDefinition)) {
                    accumulator.add(itemDefinition);
                    collectCompositeItemDefinitions(itemDefinition.getItemComponent(), accumulator);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy