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

se.cambio.cds.util.ElementInstanceCollectionManager Maven / Gradle / Ivy

package se.cambio.cds.util;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.openehr.rm.datatypes.basic.DataValue;
import org.openehr.rm.datatypes.quantity.DvOrdinal;
import org.openehr.rm.datatypes.quantity.datetime.DvDateTime;
import org.openehr.rm.datatypes.text.CodePhrase;
import org.openehr.rm.datatypes.text.DvCodedText;
import org.openehr.rm.datatypes.text.Match;
import org.openehr.rm.datatypes.text.TermMapping;
import org.slf4j.LoggerFactory;
import se.cambio.cds.gdl.model.Binding;
import se.cambio.cds.gdl.model.Guide;
import se.cambio.cds.gdl.model.TermBinding;
import se.cambio.cds.gdl.model.expression.ExpressionItem;
import se.cambio.cds.gdl.model.expression.OperatorKind;
import se.cambio.cds.model.facade.execution.vo.GeneratedArchetypeReference;
import se.cambio.cds.model.facade.execution.vo.GeneratedElementInstance;
import se.cambio.cds.model.facade.execution.vo.PredicateGeneratedElementInstance;
import se.cambio.cds.model.facade.execution.vo.RuleReference;
import se.cambio.cds.model.instance.ArchetypeReference;
import se.cambio.cds.model.instance.ElementInstance;
import se.cambio.cm.controller.terminology.TerminologyService;
import se.cambio.openehr.util.OpenEHRConst;
import se.cambio.openehr.util.misc.DataValueGenerator;

import java.util.*;

public class ElementInstanceCollectionManager {

    private TerminologyService terminologyService;

    public ElementInstanceCollectionManager(TerminologyService terminologyService) {
        this.terminologyService = terminologyService;
    }

    public static boolean isEmpty(ArchetypeReference ar) {
        for (String idElement : ar.getElementInstancesMap().keySet()) {
            ElementInstance ei = ar.getElementInstancesMap().get(idElement);
            if (ei.getDataValue() != null) {
                return false;
            }
        }
        return true;
    }

    public static ArchetypeReference getEmptyArchetypeReference(Set archetypeReferences) {
        for (ArchetypeReference archetypeReference : archetypeReferences) {
            if (isEmpty(archetypeReference)) {
                return archetypeReference;
            }
        }
        return null;
    }

    public static boolean containsAll(ArchetypeReference ar1, ArchetypeReference ar2) {
        return ar1.getElementInstancesMap().keySet().containsAll(ar2.getElementInstancesMap().keySet());
    }

    boolean matchAndFill(GeneratedArchetypeReference ar1, ArchetypeReference ar2, Map guideMap, Calendar date) {
        Collection emptyElementInstances = new ArrayList();
        boolean matches = matches(ar1, ar2, guideMap, date);
        if (!matches) {
            return false;
        } else {
            if (ar2 instanceof GeneratedArchetypeReference) {
                for (String idElement : ar1.getElementInstancesMap().keySet()) {
                    ElementInstance ei1 = ar1.getElementInstancesMap().get(idElement);
                    ElementInstance ei2 = ar2.getElementInstancesMap().get(idElement);
                    if (!(ei1 instanceof PredicateGeneratedElementInstance) && ei2 == null) {
                        ei2 = ei1.clone();
                        emptyElementInstances.add(ei2);
                    }
                    if (ei1 instanceof GeneratedElementInstance && ei2 instanceof GeneratedElementInstance) {
                        ((GeneratedElementInstance) ei2).getRuleReferences().addAll(((GeneratedElementInstance) ei1).getRuleReferences());
                    }
                }
                for (ElementInstance elementInstance : emptyElementInstances) {
                    elementInstance.setArchetypeReference(ar2);
                }
            }
        }
        return true;
    }

    public boolean matches(GeneratedArchetypeReference ar1, ArchetypeReference ar2, Map guideMap, Calendar date) {
        if (!ar1.getIdArchetype().equals(ar2.getIdArchetype())) {
            return false;
        } else {
            for (String idElement : ar1.getElementInstancesMap().keySet()) {
                ElementInstance ei1 = ar1.getElementInstancesMap().get(idElement);
                ElementInstance ei2 = ar2.getElementInstancesMap().get(idElement);
                if (ei1 instanceof PredicateGeneratedElementInstance) {
                    if (ei2 != null) {
                        OperatorKind operatorKind = ((PredicateGeneratedElementInstance) ei1).getOperatorKind();
                        Set guideIds = new HashSet<>();
                        DataValue dv = getResolveDataValueIfNeeded(guideMap, date, ei1, guideIds);
                        DataValue dv2 = getResolveDataValueIfNeeded(guideMap, date, ei2, guideIds);
                        Collection guides = getGuides(guideMap, guideIds);
                        if (!matches(dv, dv2, operatorKind, guides)) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            }
            return true;
        }
    }

    public boolean matches(
            DataValue dv1,
            DataValue dv2,
            OperatorKind operatorKind,
            Collection guides) {
        if (OperatorKind.IS_A.equals(operatorKind)) {
            if (dv1 instanceof DvCodedText && dv2 instanceof DvCodedText) {
                CodePhrase elementCodePhrase = ((DvCodedText) dv2).getDefiningCode();
                CodePhrase predicateCodePhrase = ((DvCodedText) dv1).getDefiningCode();
                Set resolvedCodePhrases = getResolvedCodePhrases(guides, predicateCodePhrase);
                Set resolvedElementCodePhrases = getResolvedCodePhrases(guides, elementCodePhrase);
                if (!resolvedCodePhrases.isEmpty() && !resolvedElementCodePhrases.isEmpty()) {
                    for (CodePhrase resolvedElementCodePhrase : resolvedElementCodePhrases) {
                        try {
                            boolean isSubclass = terminologyService.isSubclassOf(resolvedElementCodePhrase, resolvedCodePhrases);
                            if (!isSubclass) {
                                return false;
                            }
                        } catch (Exception ex) {
                            LoggerFactory.getLogger(ElementInstanceCollectionManager.class).warn(ex.getMessage());
                            return false;
                        }
                    }
                    return true;
                } else {
                    return false;
                }
            }
        } else if (OperatorKind.EQUALITY.equals(operatorKind)) {
            return DVUtil.equalDVs(dv1, dv2);
        } else if (OperatorKind.INEQUAL.equals(operatorKind)) {
            return !DVUtil.equalDVs(dv1, dv2);
        } else if (OperatorKind.MAX.equals(operatorKind) || (OperatorKind.MIN.equals(operatorKind))) {
            return dv2 != null;
        } else if (OperatorKind.GREATER_THAN.equals(operatorKind)) {
            return dv2 != null && DVUtil.compareDVs(dv1, dv2) < 0;
        } else if (OperatorKind.GREATER_THAN_OR_EQUAL.equals(operatorKind)) {
            return dv2 != null && DVUtil.compareDVs(dv1, dv2) <= 0;
        } else if (OperatorKind.LESS_THAN.equals(operatorKind)) {
            return dv2 != null && DVUtil.compareDVs(dv1, dv2) > 0;
        } else if (OperatorKind.LESS_THAN_OR_EQUAL.equals(operatorKind)) {
            return dv2 != null && DVUtil.compareDVs(dv1, dv2) >= 0;
        }
        return false;
    }

    private static Set getResolvedCodePhrases(Collection guides, CodePhrase predicateCodePhrase) {
        if (!"local".equals(predicateCodePhrase.getTerminologyId().getValue())) {
            return Collections.singleton(predicateCodePhrase);
        }
        Set codePhrases = new HashSet<>();
        if (guides != null) {
            for (Guide guide : guides) {
                if (guide.getOntology().getTermBindings() != null) {
                    for (String terminologyId : guide.getOntology().getTermBindings().keySet()) {
                        TermBinding termBinding = guide.getOntology().getTermBindings().get(terminologyId);
                        if (termBinding != null) {
                            Binding binding = termBinding.getBindings().get(predicateCodePhrase.getCodeString());
                            if (binding != null && binding.getCodes() != null) {
                                codePhrases.addAll(binding.getCodes());
                            }
                        }
                    }
                }
            }
        } else {
            codePhrases.add(predicateCodePhrase);
        }
        return codePhrases;
    }

    private static boolean isSameCode(CodePhrase elementCodePhrase, CodePhrase codePhrase) {
        return codePhrase.getCodeString().equals(elementCodePhrase.getCodeString());
    }

    private static boolean isLocalTerminology(CodePhrase elementCodePhrase) {
        return "local".equals(elementCodePhrase.getTerminologyId().getValue());
    }

    public static DataValue resolvePredicate(DataValue dv, OperatorKind op, Collection guides, Calendar date) {
        if (OperatorKind.IS_A.equals(op)) {
            if (dv instanceof DvCodedText) {
                DvCodedText dvCodedText = (DvCodedText) dv;
                return getResolvedCodedText(dvCodedText, guides);
            } else if (dv instanceof DvOrdinal) {
                return dv;
            } else {
                LoggerFactory.getLogger(ElementInstanceCollectionManager.class).warn("Not a coded text '" + dv + "'");
                return null;
            }
        } else if (dv instanceof CurrentTimeExpressionDataValue) {
            return getResolvedCurrentDateTime(dv, date);
        } else {
            return dv;
        }
    }

    private static DataValue getResolvedCurrentDateTime(DataValue dv, Calendar date) {
        CurrentTimeExpressionDataValue ctedv = ((CurrentTimeExpressionDataValue) dv);
        ExpressionItem expressionItem = ctedv.getExpressionItem();
        String expStr = ExpressionUtil.getArithmeticExpressionStr(null, expressionItem, null);
        date = (date != null ? date : Calendar.getInstance());
        DvDateTime currentDateTime = DataValueGenerator.toDvDateTime(date);
        JexlEngine engine = new JexlEngine();
        engine.setStrict(true);
        Expression expression = engine.createExpression(expStr);
        JexlContext context = new MapContext();
        context.set("$" + OpenEHRConst.CURRENT_DATE_TIME_ID, currentDateTime);
        context.set("DVUtil", new DVUtil());
        context.set("Math", new MathFunctionProxy());
        context.set("e", Math.E);
        context.set("pi", Math.PI);
        Object obj = expression.evaluate(context);
        if (obj instanceof Double) {
            obj = ((Double) obj).longValue(); //In dates we never need double value
        }
        String attribute = ctedv.getAttribute();
        currentDateTime = (DvDateTime) DataValueGenerator.createDV(currentDateTime, attribute, obj);
        return currentDateTime;
    }

    private static DataValue getResolvedCodedText(DvCodedText dv, Collection guides) {
        if ("local".equalsIgnoreCase(dv.getTerminologyId())
                && dv.getCode() != null
                && dv.getCode().startsWith("gt")) {
            if (guides != null) {
                for (Guide guide : guides) {
                    DvCodedText resolvedCodedText = getResolvedCodedText(dv, guide);
                    if (resolvedCodedText != null) {
                        return resolvedCodedText;
                    }
                }
            }
            //If reaches here, no terminology was found (problem)
            String message = "No terminology binding for '" + dv + "' was found! (num guidelines=" + (guides == null ? "0" : guides.size()) + ")";
            //ExceptionHandler.handle(new InternalErrorException(new Exception(message)));
            LoggerFactory.getLogger(ElementInstanceCollectionManager.class).warn(message);
            return null;
        } else {
            return dv;
        }
    }

    private static DvCodedText getResolvedCodedText(DvCodedText dvCT, Guide guide) {
        CodePhrase cf = null;
        List mappings = new ArrayList<>();
        if (guide.getOntology().getTermBindings() != null) {
            for (Map.Entry entry : guide.getOntology().getTermBindings().entrySet()) {
                TermBinding termBinding = entry.getValue();
                if (termBinding != null) {
                    Binding binding = termBinding.getBindings().get(dvCT.getDefiningCode().getCodeString());
                    if (binding != null && binding.getCodes() != null && !binding.getCodes().isEmpty()) {
                        cf = binding.getCodes().get(0);
                        for (CodePhrase codePhrase : binding.getCodes()) {
                            mappings.add(new TermMapping(codePhrase, Match.EQUIVALENT, null, null));
                        }
                    }
                }
            }
        }
        if (!mappings.isEmpty() && cf != null) {
            return new DvCodedText(dvCT.getValue(), mappings, null, null, null, null, cf, null);
        } else {
            return dvCT;
        }
    }


    private static Collection getGuides(Map guideMap, Set guideIds) {
        Collection guides = new ArrayList<>();
        for (String guideId : guideIds) {
            Guide guide = guideMap.get(guideId);
            guides.add(guide);
        }
        return guides;
    }

    private static DataValue getResolveDataValueIfNeeded(Map guideMap, Calendar date, ElementInstance ei, Set guideIds) {
        DataValue dv = ei.getDataValue();
        if (ei instanceof PredicateGeneratedElementInstance) {
            Set localGuideIds = new HashSet<>();
            PredicateGeneratedElementInstance pgei = ((PredicateGeneratedElementInstance) ei);
            for (RuleReference ruleReference : pgei.getRuleReferences()) {
                Guide guide = guideMap.get(ruleReference.getGuideId());
                if (guide == null) {
                    LoggerFactory.getLogger(ElementInstanceCollectionManager.class).warn("Null guideline for rule reference '" + ruleReference + "'");
                } else {
                    localGuideIds.add(guide.getId());
                }
            }
            Collection localGuides = new ArrayList<>();
            for (String guideId : localGuideIds) {
                Guide guide = guideMap.get(guideId);
                localGuides.add(guide);
            }
            if (pgei.getOperatorKind().equals(OperatorKind.IS_A)) {
                dv = ei.getDataValue();
            } else {
                dv = resolvePredicate(ei.getDataValue(), pgei.getOperatorKind(), localGuides, date);
            }
            guideIds.addAll(localGuideIds);
        }
        return dv;
    }

}
/*
 *  ***** BEGIN LICENSE BLOCK *****
 *  Version: MPL 2.0/GPL 2.0/LGPL 2.1
 *
 *  The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
 *
 *  Software distributed under the License is distributed on an 'AS IS' basis,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 *  for the specific language governing rights and limitations under the
 *  License.
 *
 *
 *  The Initial Developers of the Original Code are Iago Corbal and Rong Chen.
 *  Portions created by the Initial Developer are Copyright (C) 2012-2013
 *  the Initial Developer. All Rights Reserved.
 *
 *  Contributor(s):
 *
 * Software distributed under the License is distributed on an 'AS IS' basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 *  ***** END LICENSE BLOCK *****
 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy