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

org.cqframework.cql.cql2elm.model.InstantiationContextImpl Maven / Gradle / Ivy

Go to download

The cql-to-elm library for the Clinical Quality Language Java reference implementation

There is a newer version: 3.18.0
Show newest version
package org.cqframework.cql.cql2elm.model;

import java.util.ArrayList;
import java.util.Map;
import org.hl7.cql.model.*;

public class InstantiationContextImpl implements InstantiationContext {
    public InstantiationContextImpl(
            Map typeMap,
            OperatorMap operatorMap,
            ConversionMap conversionMap,
            boolean allowPromotionAndDemotion) {
        if (typeMap == null) {
            throw new IllegalArgumentException("typeMap is null");
        }

        if (operatorMap == null) {
            throw new IllegalArgumentException("operatorMap is null");
        }

        if (conversionMap == null) {
            throw new IllegalArgumentException("conversionMap is null");
        }

        this.typeMap = typeMap;
        this.operatorMap = operatorMap;
        this.conversionMap = conversionMap;
        this.allowPromotionAndDemotion = allowPromotionAndDemotion;
    }

    private Map typeMap;
    private OperatorMap operatorMap;
    private ConversionMap conversionMap;
    private boolean allowPromotionAndDemotion;
    private int conversionScore;

    public int getConversionScore() {
        return conversionScore;
    }

    @Override
    public boolean isInstantiable(TypeParameter parameter, DataType callType) {
        // If the type is not yet bound, bind it to the call type.
        DataType boundType = typeMap.get(parameter);
        if (boundType == null) {
            if (parameter.canBind(callType)) {
                typeMap.put(parameter, callType);
                return true;
            } else {
                return false;
            }
        } else {
            // If the type is bound, and is a super type of the call type, return true;
            if (boundType.isSuperTypeOf(callType) || callType.isCompatibleWith(boundType)) {
                return true;
            } else if (callType.isSuperTypeOf(boundType) || boundType.isCompatibleWith(callType)) {
                // If the call type is a super type of the bound type, switch the bound type for this parameter to the
                // call type
                if (parameter.canBind(callType)) {
                    typeMap.put(parameter, callType);
                    return true;
                } else {
                    return false;
                }
            } else {
                // If there is an implicit conversion path from the call type to the bound type, return true
                Conversion conversion =
                        conversionMap.findConversion(callType, boundType, true, allowPromotionAndDemotion, operatorMap);
                if (conversion != null) {
                    // if the conversion is a list promotion, switch the bound type to the call type
                    if (boundType instanceof ListType) {
                        ListType boundListType = (ListType) boundType;
                        if (boundListType.getElementType().isSuperTypeOf(callType)
                                || callType.isCompatibleWith(boundListType.getElementType())) {
                            if (parameter.canBind(callType)) {
                                typeMap.put(parameter, callType);
                                conversionScore -=
                                        ConversionMap.ConversionScore.ListPromotion
                                                .score(); // This removes the list promotion
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                    return true;
                }

                // If there is an implicit conversion path from the bound type to the call type
                conversion =
                        conversionMap.findConversion(boundType, callType, true, allowPromotionAndDemotion, operatorMap);
                if (conversion != null) {
                    // switch the bound type to the call type and return true
                    if (parameter.canBind(callType)) {
                        typeMap.put(parameter, callType);
                        conversionScore -= ((conversion.getToType() instanceof SimpleType)
                                ? ConversionMap.ConversionScore.SimpleConversion.score()
                                : ConversionMap.ConversionScore.ComplexConversion
                                        .score()); // This removes the conversion from the instantiation
                        return true;
                    } else {
                        return false;
                    }
                }

                // Find the first supertype that is a supertype of both types
                /*
                // This code doesn't play well with generic signatures... it ends up treating everything like an Any, resulting in all sorts of surprising resolutions
                DataType boundCommonSuperType = boundType.getCommonSuperTypeOf(callType);
                DataType callCommonSuperType = callType.getCommonSuperTypeOf(boundType);
                if (boundCommonSuperType != null && callCommonSuperType != null) {
                    if (boundCommonSuperType.isSuperTypeOf(callCommonSuperType)) {
                        if (parameter.canBind(boundCommonSuperType)) {
                            typeMap.put(parameter, boundCommonSuperType);
                            return true;
                        }
                        else {
                            return false;
                        }
                    }
                    else {
                        if (parameter.canBind(callCommonSuperType)) {
                            typeMap.put(parameter, callCommonSuperType);
                            return true;
                        }
                        else {
                            return false;
                        }
                    }
                }
                */
            }
        }

        return false;
    }

    @Override
    public DataType instantiate(TypeParameter parameter) {
        DataType result = typeMap.get(parameter);
        if (result == null) {
            throw new IllegalArgumentException(
                    String.format("Could not resolve type parameter %s.", parameter.getIdentifier()));
        }

        return result;
    }

    @Override
    public Iterable getIntervalConversionTargets(DataType callType) {
        ArrayList results = new ArrayList();
        for (Conversion c : conversionMap.getConversions(callType)) {
            if (c.getToType() instanceof IntervalType) {
                results.add((IntervalType) c.getToType());
                conversionScore += ConversionMap.ConversionScore.ComplexConversion.score();
            }
        }

        if (results.isEmpty()) {
            for (Conversion c : conversionMap.getGenericConversions()) {
                if (c.getOperator() != null) {
                    if (c.getToType() instanceof IntervalType) {
                        // instantiate the generic...
                        InstantiationResult instantiationResult = ((GenericOperator) c.getOperator())
                                .instantiate(new Signature(callType), operatorMap, conversionMap, false);
                        Operator operator = instantiationResult.getOperator();
                        // TODO: Consider impact of conversion score of the generic instantiation on this conversion
                        // score
                        if (operator != null) {
                            operatorMap.addOperator(operator);
                            Conversion conversion = new Conversion(operator, true);
                            conversionMap.add(conversion);
                            results.add((IntervalType) conversion.getToType());
                        }
                    }
                }
            }
        }

        // Add interval promotion if no other conversion is found
        if (results.isEmpty()) {
            if (!(callType instanceof IntervalType)
                    && operatorMap.isPointType(callType)
                    && (allowPromotionAndDemotion || conversionMap.isIntervalPromotionEnabled())) {
                results.add(new IntervalType(callType));
                conversionScore += ConversionMap.ConversionScore.IntervalPromotion.score();
            }
        }

        return results;
    }

    @Override
    public Iterable getListConversionTargets(DataType callType) {
        ArrayList results = new ArrayList();
        for (Conversion c : conversionMap.getConversions(callType)) {
            if (c.getToType() instanceof ListType) {
                results.add((ListType) c.getToType());
                conversionScore += ConversionMap.ConversionScore.ComplexConversion.score();
            }
        }

        if (results.isEmpty()) {
            for (Conversion c : conversionMap.getGenericConversions()) {
                if (c.getOperator() != null) {
                    if (c.getToType() instanceof ListType) {
                        // instantiate the generic...
                        InstantiationResult instantiationResult = ((GenericOperator) c.getOperator())
                                .instantiate(new Signature(callType), operatorMap, conversionMap, false);
                        Operator operator = instantiationResult.getOperator();
                        // TODO: Consider impact of conversion score of the generic instantiation on this conversion
                        // score
                        if (operator != null) {
                            operatorMap.addOperator(operator);
                            Conversion conversion = new Conversion(operator, true);
                            conversionMap.add(conversion);
                            results.add((ListType) conversion.getToType());
                        }
                    }
                }
            }
        }

        // NOTE: FHIRPath support
        // Add list promotion if no other conversion is found
        if (results.isEmpty()) {
            if (!(callType instanceof ListType)
                    && (allowPromotionAndDemotion || conversionMap.isListPromotionEnabled())) {
                results.add(new ListType(callType));
                conversionScore += ConversionMap.ConversionScore.ListPromotion.score();
            }
        }

        return results;
    }

    @Override
    public Iterable getSimpleConversionTargets(DataType callType) {
        ArrayList results = new ArrayList();
        for (Conversion c : conversionMap.getConversions(callType)) {
            if (c.getToType() instanceof SimpleType) {
                results.add((SimpleType) c.getToType());
                conversionScore += ConversionMap.ConversionScore.SimpleConversion.score();
            }
        }

        if (results.isEmpty()) {
            for (Conversion c : conversionMap.getGenericConversions()) {
                if (c.getOperator() != null) {
                    if (c.getToType() instanceof SimpleType) {
                        InstantiationResult instantiationResult = ((GenericOperator) c.getOperator())
                                .instantiate(new Signature(callType), operatorMap, conversionMap, false);
                        Operator operator = instantiationResult.getOperator();
                        // TODO: Consider impact of conversion score of the generic instantiation on this conversion
                        // score
                        if (operator != null) {
                            operatorMap.addOperator(operator);
                            Conversion conversion = new Conversion(operator, true);
                            conversionMap.add(conversion);
                            results.add((SimpleType) conversion.getToType());
                        }
                    }
                }
            }
        }

        // Add interval demotion if no other conversion is found
        if (results.isEmpty()) {
            if (callType instanceof IntervalType) {
                IntervalType callIntervalType = (IntervalType) callType;
                if (callIntervalType.getPointType() instanceof SimpleType
                        && (allowPromotionAndDemotion || conversionMap.isIntervalDemotionEnabled())) {
                    results.add((SimpleType) callIntervalType.getPointType());
                    conversionScore += ConversionMap.ConversionScore.IntervalDemotion.score();
                }
            }
        }

        // NOTE: FHIRPath Support
        // Add list demotion if no other conversion is found
        if (results.isEmpty()) {
            if (callType instanceof ListType) {
                ListType callListType = (ListType) callType;
                if (callListType.getElementType() instanceof SimpleType
                        && (allowPromotionAndDemotion || conversionMap.isListDemotionEnabled())) {
                    results.add((SimpleType) callListType.getElementType());
                    conversionScore += ConversionMap.ConversionScore.ListDemotion.score();
                }
            }
        }

        return results;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy