package se.cambio.cds.gdl.converters.drools; import org.apache.log4j.Logger; import org.openehr.rm.datatypes.basic.DataValue; import org.openehr.rm.datatypes.text.CodePhrase; import se.cambio.cds.controller.execution.DroolsExecutionManager; import se.cambio.cds.gdl.model.*; import se.cambio.cds.gdl.model.expression.*; import se.cambio.cds.model.instance.ArchetypeReference; import se.cambio.cds.util.export.DVDefSerializer; import se.cambio.cds.util.ExpressionUtil; import se.cambio.cds.util.RefStat; import se.cambio.openehr.controller.session.data.ArchetypeElements; import se.cambio.openehr.controller.session.data.ArchetypeManager; import se.cambio.cm.model.archetype.vo.ArchetypeElementVO; import se.cambio.openehr.util.OpenEHRConst; import se.cambio.openehr.util.OpenEHRDataValues; import se.cambio.openehr.util.exceptions.InternalErrorException; import java.util.*; public class GDLDroolsConverter { private static Logger log = Logger.getLogger(GDLDroolsConverter.class); private final ArchetypeManager archetypeManager; private Guide guide; // Drools keywords private static String RULE = "rule"; private static String WHEN = "when"; private static String THEN = "then"; private static String END = "end"; private static String DEFAULT_CONFIG = "no-loop true"; private static String AGENDA_GROUP = "agenda-group"; private static String SALIENCE = "salience"; private static String TAB = " "; private static String AGENDA_GROUP_LINK_ID = "*agenda-group-link"; private Map<String, String> _gtElementToWholeDefinition = new HashMap<String, String>(); private Map<String, String> _gtElementToDefinition = new HashMap<String, String>(); private Map<String, String> _gtElementToElementId = new HashMap<String, String>(); private int creationIndex = 0; public GDLDroolsConverter(Guide guide, ArchetypeManager archetypeManager) { this.guide = guide; this.archetypeManager = archetypeManager; } public String convertToDrools() throws InternalErrorException{ StringBuffer sb = new StringBuffer(); sb.append(getGuideHeader()); Map<String, ArchetypeReference> archetypeReferenceMap = new HashMap<String, ArchetypeReference>(); Map<String, ArchetypeElementVO> elementMap = new HashMap<String, ArchetypeElementVO>(); // Add currentTime elementMap.put(OpenEHRConst.CURRENT_DATE_TIME_ID, ArchetypeElements.CURRENT_DATE_TIME); Map<Integer, String> archetypeBindingIndexToDefinition = new HashMap<Integer, String>(); Map<String, Integer> gtElementToArchetypeBindingIndex = new HashMap<String, Integer>(); Map<String, ArchetypeBinding> archetypeBindings = guide.getDefinition().getArchetypeBindings(); fillDefinitions( archetypeBindings!=null?archetypeBindings.values():null, archetypeBindingIndexToDefinition, gtElementToArchetypeBindingIndex, archetypeReferenceMap, elementMap); Map<RefStat, Set<String>> preconditionStats = initStats(); String preconditionStr = null; if (guide.getDefinition().getPreConditionExpressions() != null) { preconditionStr = convertExpressionsToMVEL( guide.getDefinition().getPreConditionExpressions(), archetypeReferenceMap, elementMap, preconditionStats); } if (guide.getDefinition().getRules()!=null){ for (Rule rule : guide.getDefinition().getRules().values()) { Map<RefStat, Set<String>> ruleStats = initStats(); String whenStr = convertExpressionsToMVEL(rule.getWhenStatements(), archetypeReferenceMap, elementMap, ruleStats); String thenStr = convertAssigmentExpressionsToMVEL( rule.getThenStatements(), archetypeReferenceMap, elementMap, ruleStats); Set<String> gtCodesRef = new HashSet<String>(); gtCodesRef.addAll(ruleStats.get(RefStat.REFERENCE)); gtCodesRef.addAll(preconditionStats.get(RefStat.REFERENCE)); gtCodesRef.remove(OpenEHRConst.CURRENT_DATE_TIME_ID); String definition = getDefinitionForRule(gtCodesRef, archetypeBindingIndexToDefinition, gtElementToArchetypeBindingIndex); ruleStats.get(RefStat.ATT_SET_REF).remove(OpenEHRConst.CURRENT_DATE_TIME_ID); String hasValueChecks = getHasValueStr(ruleStats.get(RefStat.ATT_SET_REF)); //Check if a function is used, add whatever extra code necessary for it (for now, just count) Set<String> functionsRefs = new HashSet<String>(); functionsRefs.addAll(ruleStats.get(RefStat.ATT_FUNCTIONS)); functionsRefs.addAll(preconditionStats.get(RefStat.ATT_FUNCTIONS)); String functionExtraCode = getFunctionsExtraCode(functionsRefs); sb.append(RULE + " \"" + guide.getId() + "/" + rule.getId() + "\"\n"); String guideSalienceId = DroolsExecutionManager.getGuideSalienceId(guide.getId()); sb.append(SALIENCE + " "+ guideSalienceId + " + " + rule.getPriority() + "\n"); sb.append(DEFAULT_CONFIG + "\n"); sb.append(WHEN + "\n"); if (definition != null){ sb.append(definition); } if (functionExtraCode != null){ sb.append(functionExtraCode); } if (hasValueChecks != null){ sb.append(hasValueChecks); } if (preconditionStr != null){ sb.append(preconditionStr); } if (whenStr != null){ sb.append(whenStr); } sb.append(THEN + "\n"); if (thenStr != null){ sb.append(thenStr); } sb.append(END + "\n\n"); } } return sb.toString(); } private void fillDefinitions( Collection<ArchetypeBinding> archetypeBindings, Map<Integer, String> archetypeBindingIndexToDefinition, Map<String, Integer> gtElementToArchetypeBindingIndex, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap) throws InternalErrorException { int arCount = 0; int predicateCount = 0; String arID = "archetypeReference"; if (archetypeBindings!=null){ for(ArchetypeBinding archetypeBinding: archetypeBindings){ arCount++; StringBuffer archetypeBindingMVELSB = new StringBuffer(); archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB.append("$"+arID+arCount); String idDomain = archetypeBinding.getDomain(); archetypeBindingMVELSB.append(":ArchetypeReference"); archetypeBindingMVELSB.append("("); if (idDomain!=null){ archetypeBindingMVELSB.append("idDomain==\""+idDomain+"\", "); } String archetypeId = archetypeBinding.getArchetypeId(); String templateId = archetypeBinding.getTemplateId(); archetypeBindingMVELSB.append("idArchetype==\""+archetypeId+"\""); /* if ((Domains.CDS_ID.equals(archetypeBinding.getDomain()))&& archetypeBinding.getId()!=null){ archetypeBindingMVELSB.append(", idTemplate==\""+templateId+"\""); } */ archetypeBindingMVELSB.append(")\n"); String gtCode = archetypeBinding.getId(); archetypeReferenceMap.put(gtCode, new ArchetypeReference(idDomain, archetypeId, templateId)); // Predicates if (archetypeBinding.getPredicateStatements() != null) { for (ExpressionItem expressionItem : archetypeBinding.getPredicateStatements()) { if (expressionItem instanceof BinaryExpression) { BinaryExpression binaryExpression = (BinaryExpression) expressionItem; if (binaryExpression.getLeft() instanceof Variable){ predicateCount++; Variable variable = (Variable) binaryExpression .getLeft(); if (binaryExpression.getRight() instanceof ConstantExpression) { ConstantExpression constantExpression = (ConstantExpression) binaryExpression.getRight(); String idElement = archetypeBinding .getArchetypeId() + variable.getPath(); archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB.append("$predicate" + predicateCount); archetypeBindingMVELSB.append(":ElementInstance(id==\"" + idElement + "\", archetypeReference==$" + arID+arCount + ")\n"); ArchetypeElementVO archetypeElement = archetypeManager.getArchetypeElements().getArchetypeElement( archetypeBinding.getTemplateId(), idElement); if (archetypeElement==null){ throw new InternalErrorException(new Exception("Element not found '"+idElement+"'"+(archetypeBinding.getTemplateId()!=null?"("+archetypeBinding.getTemplateId()+")":""))); } String rmType = archetypeElement.getRMType(); archetypeBindingMVELSB.append(TAB); String dvStr = "null"; if (!constantExpression.getValue().equals("null")){ dvStr = DVDefSerializer.getDVInstantiation(DataValue.parseValue(rmType+ ","+ constantExpression.getValue())); } archetypeBindingMVELSB .append("eval("+ getOperatorMVELLine( "$predicate"+ predicateCount, binaryExpression.getOperator(), dvStr, true)+ ")\n"); }else if (binaryExpression.getRight() instanceof ExpressionItem) { String path = variable.getPath(); String attribute = path.substring(path.lastIndexOf("/value/")+7, path.length()); path = path.substring(0, path.length()-attribute.length()-7); String idElement = archetypeBinding .getArchetypeId() + path; archetypeBindingMVELSB.append(TAB); String predicateHandle = "predicate"+ predicateCount; archetypeBindingMVELSB.append("$"+predicateHandle); archetypeBindingMVELSB .append(":ElementInstance(id==\"" + idElement +"\", " + "dataValue!=null, " + "archetypeReference==$" + arID+arCount + ")\n"); ArchetypeElementVO archetypeElement = archetypeManager.getArchetypeElements().getArchetypeElement( archetypeBinding.getTemplateId(), idElement); if (archetypeElement!=null){ String rmName = archetypeElement.getRMType(); String aritmeticExpStr = //We cast it to long because all elements from CurrentTime fit into this class, but we must make it more generic (TODO) "((long)"+ExpressionUtil.getArithmeticExpressionStr(elementMap, binaryExpression.getRight(), null)+")"; archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB.append("eval("); Variable var = new Variable(predicateHandle, predicateHandle, path, attribute); String varCall = ExpressionUtil.getVariableWithAttributeStr(rmName,var); archetypeBindingMVELSB.append("("); if (isString(rmName, attribute)){ archetypeBindingMVELSB.append(getAttributeOperatorMVELLine(varCall, binaryExpression.getOperator(), aritmeticExpStr)); }else{ archetypeBindingMVELSB.append(varCall); archetypeBindingMVELSB.append(binaryExpression.getOperator().getSymbol()); archetypeBindingMVELSB.append(aritmeticExpStr); } archetypeBindingMVELSB.append("))\n"); }else{ throw new InternalErrorException(new Exception("Element not found '"+idElement+"'"+(archetypeBinding.getTemplateId()!=null?"("+archetypeBinding.getTemplateId()+")":""))); } } } }else if (expressionItem instanceof UnaryExpression) { UnaryExpression unaryExpression = (UnaryExpression) expressionItem; predicateCount++; Variable variable = (Variable) unaryExpression.getOperand(); String idElement = archetypeBinding .getArchetypeId() + variable.getPath(); archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB .append("ElementInstance(id==\"" + idElement + "\", archetypeReference==$" + arID+arCount +", " + "predicate || dataValue instanceof Comparable, " + "$predDV"+ predicateCount+":dataValue" + ")\n"); ArchetypeElementVO archetypeElement = archetypeManager.getArchetypeElements().getArchetypeElement( archetypeBinding.getTemplateId(), idElement); OperatorKind op = unaryExpression.getOperator(); String opStr = null; if (OperatorKind.MAX.equals(op)){ opStr=">"; }else if (OperatorKind.MIN.equals(op)){ opStr="<"; }else{ Logger.getLogger(GDLDroolsConverter.class).warn("Guide="+guide.getId()+", Operator for predicate '"+op+"' is not valid."); } if (archetypeElement!=null && opStr!=null){ String predAuxDef = getComparisonPredicateChecks(archetypeBinding, predicateCount); String predicateArchetypeRef = ""; archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB.append("not(\n"); if (predAuxDef!=null){ archetypeBindingMVELSB.append(predAuxDef); predicateArchetypeRef = "archetypeReference==$archetypeReferencePredicate"+predicateCount+","; } archetypeBindingMVELSB.append(TAB); archetypeBindingMVELSB .append("ElementInstance(id==\"" + idElement + "\", " + predicateArchetypeRef + "DVUtil.areDomainsCompatible($"+arID+arCount +".getIdDomain(), archetypeReference.getIdDomain())," + "DVUtil.checkMaxMin($predDV"+ predicateCount+", dataValue, \""+op.getSymbol()+"\")" +"))\n"); }else{ throw new InternalErrorException(new Exception("Element not found '"+idElement+"'")); } } } } archetypeBindingIndexToDefinition.put(arCount, archetypeBindingMVELSB.toString()); Map<String, ElementBinding> elementBindingsMap = archetypeBinding.getElements(); if (elementBindingsMap!=null){ for (ElementBinding element :elementBindingsMap.values()) { StringBuffer elementDefinitionSB = new StringBuffer(); String idElement = archetypeBinding.getArchetypeId() + element.getPath(); ArchetypeElementVO value = archetypeManager.getArchetypeElements().getArchetypeElement( archetypeBinding.getTemplateId(), idElement); elementMap.put(element.getId(), value); elementDefinitionSB.append("ElementInstance(id==\""+idElement+"\", archetypeReference==$"+arID+arCount+")"); _gtElementToDefinition.put(element.getId(), elementDefinitionSB.toString()); _gtElementToElementId.put(element.getId(), idElement); gtElementToArchetypeBindingIndex.put(element.getId(), arCount); } } } } } private String getComparisonPredicateChecks(ArchetypeBinding archetypeBinding, Integer predicateCount){ StringBuffer sb = new StringBuffer(); for (ExpressionItem expressionItem : archetypeBinding.getPredicateStatements()) { if (expressionItem instanceof BinaryExpression) { BinaryExpression binaryExpression = (BinaryExpression) expressionItem; if (binaryExpression.getLeft() instanceof Variable && binaryExpression.getRight() instanceof ConstantExpression) { Variable variable = (Variable) binaryExpression.getLeft(); ConstantExpression constantExpression = (ConstantExpression) binaryExpression.getRight(); String idElement = archetypeBinding.getArchetypeId() + variable.getPath(); sb.append(TAB); sb.append("$predicateAux"+ predicateCount); sb.append(":ElementInstance(id==\""); sb.append(idElement); sb.append("\", archetypeReference==$archetypeReferencePredicate"+predicateCount+") and \n"); ArchetypeElementVO archetypeElement = archetypeManager.getArchetypeElements().getArchetypeElement( archetypeBinding.getTemplateId(), idElement); if (archetypeElement!=null){ String rmType = archetypeElement.getRMType(); String dvStr = "null"; if (!"null".equals(constantExpression.getValue())){ dvStr = DVDefSerializer.getDVInstantiation(DataValue.parseValue(rmType+ ","+ constantExpression.getValue())); } sb.append(TAB); sb.append("eval("+ getOperatorMVELLine( "$predicateAux"+ predicateCount, binaryExpression.getOperator(), dvStr, true)+ ") and \n"); } } } } String predicateDef = sb.toString(); if (!predicateDef.isEmpty()){ sb = new StringBuffer(); sb.append(TAB); sb.append("$archetypeReferencePredicate"+predicateCount+":ArchetypeReference(idDomain==\"EHR\","); sb.append("idArchetype==\""+archetypeBinding.getArchetypeId()+"\") and \n"); return sb.toString()+predicateDef; }else{ return null; } } private String getDefinitionForRule(Set<String> gtCodesRef, Map<Integer, String> archetypeBindingIndexToDefinition, Map<String, Integer> gtElementToArchetypeBindingIndex) { Map<Integer, StringBuffer> archetypeDefinitions = new HashMap<Integer, StringBuffer>(); for (String elementGtCode : gtCodesRef) { Integer archetypeBindingIndex = gtElementToArchetypeBindingIndex .get(elementGtCode); StringBuffer definition = archetypeDefinitions .get(archetypeBindingIndex); if (definition == null) { definition = new StringBuffer(); definition.append(archetypeBindingIndexToDefinition.get(archetypeBindingIndex)); archetypeDefinitions.put(archetypeBindingIndex, definition); } definition.append(TAB); definition.append("$"+elementGtCode+":"+_gtElementToDefinition.get(elementGtCode)+"\n"); } StringBuffer resultSB = new StringBuffer(); for (StringBuffer definition : archetypeDefinitions.values()) { resultSB.append(definition.toString()); } for (String elementGtCode: gtCodesRef){ Integer archetypeBindingIndex = gtElementToArchetypeBindingIndex.get(elementGtCode); _gtElementToWholeDefinition.put(elementGtCode, archetypeDefinitions.get(archetypeBindingIndex).toString()); } return resultSB.toString(); } private Map<RefStat, Set<String>> initStats() { Map<RefStat, Set<String>> stats = new HashMap<RefStat, Set<String>>(); for (RefStat refStat : RefStat.values()) { stats.put(refStat, new HashSet<String>()); } return stats; } private String convertExpressionsToMVEL( Collection<ExpressionItem> expressionItems, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { StringBuffer sb = new StringBuffer(); if (expressionItems != null) { for (ExpressionItem expressionItem : expressionItems) { sb.append(TAB); processExpressionItem(sb, expressionItem, archetypeReferenceMap, elementMap, stats); sb.append("\n"); } } return sb.toString(); } private String convertAssigmentExpressionsToMVEL( Collection<AssignmentExpression> expressionItems, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { StringBuffer sb = new StringBuffer(); if (expressionItems != null) { for (ExpressionItem expressionItem : expressionItems) { sb.append(TAB); processExpressionItem(sb, expressionItem, archetypeReferenceMap, elementMap, stats); sb.append("\n"); } for (String gtCodes : stats.get(RefStat.SET)) { sb.append(TAB); sb.append("modify($"+gtCodes+"){};\n"); } } return sb.toString(); } protected void processExpressionItem(StringBuffer sb, ExpressionItem expressionItem, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { if (expressionItem instanceof AssignmentExpression) { processAssigmentExpression(sb, (AssignmentExpression) expressionItem, archetypeReferenceMap, elementMap, stats, false); } else if (expressionItem instanceof BinaryExpression) { processBinaryExpression(sb, (BinaryExpression) expressionItem, archetypeReferenceMap, elementMap, stats); } else if (expressionItem instanceof UnaryExpression) { processUnaryExpression(sb, (UnaryExpression) expressionItem, archetypeReferenceMap, elementMap, stats); } else { throw new InternalErrorException(new Exception("Unknown expression '"+ expressionItem.getClass().getName() + "'")); } } protected void processAssigmentExpression(StringBuffer sb, AssignmentExpression assignmentExpression, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats, boolean creatingInstance) throws InternalErrorException { String gtCode = assignmentExpression.getVariable().getCode(); Variable var = assignmentExpression.getVariable(); String attribute = var.getAttribute(); processAssigmentExpression(sb, gtCode, gtCode, attribute, assignmentExpression.getAssignment(), archetypeReferenceMap, elementMap, stats, creatingInstance); } protected void processAssigmentExpression(StringBuffer sb, String gtCode, String eiId, String attribute, ExpressionItem expressionItemAux, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats, boolean creatingInstance) throws InternalErrorException { if (!CreateInstanceExpression.FUNCTION_CREATE_NAME.equals(attribute) && !creatingInstance){ stats.get(RefStat.REFERENCE).add(gtCode); stats.get(RefStat.SET).add(gtCode); } if (attribute == null) { if (expressionItemAux instanceof Variable) { Variable var2 = (Variable) expressionItemAux; String gtCodeAux = var2.getCode(); stats.get(RefStat.REFERENCE).add(gtCodeAux); sb.append("$"+eiId+".setDataValue($"+gtCodeAux+ExpressionUtil.getDataValueMethod(gtCode)+");"); sb.append("$"+eiId+".setNullFlavour(null);"); sb.append("$executionLogger.addLog(drools, $"+ eiId + ");"); } else if (expressionItemAux instanceof ConstantExpression) { String dvStr = ((ConstantExpression) expressionItemAux).getValue(); ArchetypeElementVO archetypeElementVO = elementMap.get(gtCode); if (archetypeElementVO==null){ throw new InternalErrorException(new Exception("Guide="+guide.getId()+", Unknown element for gtCode '"+gtCode+"'")); } String rmType = archetypeElementVO.getRMType(); DataValue dv = DataValue.parseValue(rmType + "," + dvStr); sb.append("$"+eiId+".setDataValue("+ DVDefSerializer.getDVInstantiation(dv)+");"); sb.append("$"+eiId+".setNullFlavour(null);"); sb.append("$executionLogger.addLog(drools, $"+eiId +");"); } else { throw new InternalErrorException(new Exception("Guide="+guide.getId()+", Unknown expression '"+expressionItemAux+"'")); } } else { if (attribute.equals(OpenEHRConst.NULL_FLAVOR_ATTRIBUTE)){ String dvStr = ((ConstantExpression) expressionItemAux).getValue(); DataValue dv = DataValue.parseValue(OpenEHRDataValues.DV_CODED_TEXT + "," + dvStr); //Map<RefStat, Set<String>> statsAux = initStats(); //???? //stats.get(RefStat.REFERENCE).addAll(statsAux.get(RefStat.REFERENCE)); //???? sb.append("$"+eiId+ ".setDataValue(null);"); sb.append("$"+eiId+".setNullFlavour("+ DVDefSerializer.getDVInstantiation(dv)+");"); sb.append("$executionLogger.addLog(drools, $"+eiId +");"); }else if (attribute.equals(CreateInstanceExpression.FUNCTION_CREATE_NAME)) { ArchetypeReference ar = archetypeReferenceMap.get(gtCode); String arId = "newAR"+creationIndex; sb.append("ArchetypeReference " + arId + " = new ArchetypeReference(\"CDS\", \"" + ar.getIdArchetype() + "\","+(ar.getIdTemplate()!=null?"\""+ar.getIdTemplate()+"\"":"null")+");\n"); sb.append(TAB); sb.append("insert("+arId+");\n"); insertAssignments(sb, arId, archetypeReferenceMap, elementMap, expressionItemAux, stats); creationIndex++; }else{ ArchetypeElementVO archetypeElementVO = elementMap.get(gtCode); if (archetypeElementVO==null){ throw new InternalErrorException(new Exception("GTCode '"+gtCode+"' not found. (guideId='"+guide.getId()+"')")); } String rmName = archetypeElementVO.getRMType(); Map<RefStat, Set<String>> statsAux = initStats(); String arithmeticExpStr = ExpressionUtil.getArithmeticExpressionStr(elementMap, expressionItemAux, statsAux); stats.get(RefStat.REFERENCE).addAll(statsAux.get(RefStat.REFERENCE)); stats.get(RefStat.REFERENCE).addAll(statsAux.get(RefStat.ATT_FUNCTIONS_REF)); stats.get(RefStat.ATT_SET_REF).addAll(statsAux.get(RefStat.REFERENCE)); stats.get(RefStat.ATT_FUNCTIONS).addAll(statsAux.get(RefStat.ATT_FUNCTIONS)); sb.append("$"+ eiId+ "."+ getAttributeSettingStr(eiId, rmName, attribute, arithmeticExpStr)+ ";"); sb.append("$"+ eiId+ ".setNullFlavour(null);"); sb.append("$executionLogger.addLog(drools, $"+ eiId + ");"); } } } private void insertAssignments( StringBuffer sb, String arId, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, ExpressionItem expressionItem, Map<RefStat, Set<String>> stats) throws InternalErrorException { if (!(expressionItem instanceof MultipleAssignmentExpression)){ throw new InternalErrorException(new Exception("Guide="+guide.getId()+", Incorrect expression inside creation expression '"+expressionItem+"'")); } MultipleAssignmentExpression multipleAssignmentExpression = (MultipleAssignmentExpression)expressionItem; int i = 0; Map<String, String> elementIdsMap = new HashMap<String, String>(); for(AssignmentExpression assignmentExpressionAux: multipleAssignmentExpression.getAssignmentExpressions()){ String gtCode = assignmentExpressionAux.getVariable().getCode(); ArchetypeElementVO archetypeElementVO = elementMap.get(gtCode); if (archetypeElementVO==null){ throw new InternalErrorException(new Exception("GTCode '"+gtCode+"' not found. (guideId='"+guide.getId()+"')")); } String elementId = archetypeElementVO.getId(); String eiId = elementIdsMap.get(elementId); if (eiId==null){ eiId = "ei"+creationIndex+"_"+i; elementIdsMap.put(elementId, eiId); sb.append(TAB); sb.append("ElementInstance $"+eiId+" = new ElementInstance(\""+elementId+"\", null, "+arId+", null, null);\n"); }else{ sb.append("\n"); } sb.append(TAB); String attribute = assignmentExpressionAux.getVariable().getAttribute(); processAssigmentExpression(sb, gtCode, eiId, attribute, assignmentExpressionAux.getAssignment(), archetypeReferenceMap, elementMap, stats, true); sb.append("insert($" + eiId + ");"); i++; } } private String getAttributeSettingStr(String gtCode, String rmName, String attributeName, String setStr) { return "setDataValue(DVUtil.createDV($" + gtCode + ",\"" + rmName + "\",\"" + attributeName + "\"," + setStr + "))"; } protected void processBinaryExpression(StringBuffer sb, BinaryExpression binaryExpression, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { if (OperatorKind.OR.equals(binaryExpression.getOperator())) { sb.append("("); processExpressionItem(sb, binaryExpression.getLeft(), archetypeReferenceMap, elementMap, stats); sb.append(" or "); processExpressionItem(sb, binaryExpression.getRight(), archetypeReferenceMap, elementMap, stats); sb.append(")"); } else if (OperatorKind.AND.equals(binaryExpression.getOperator())) { sb.append("("); processExpressionItem(sb, binaryExpression.getLeft(), archetypeReferenceMap, elementMap, stats); sb.append(" and "); processExpressionItem(sb, binaryExpression.getRight(), archetypeReferenceMap, elementMap, stats); sb.append(")"); } else if (OperatorKind.EQUALITY.equals(binaryExpression.getOperator()) || OperatorKind.INEQUAL.equals(binaryExpression.getOperator()) || OperatorKind.IS_A.equals(binaryExpression.getOperator()) || OperatorKind.IS_NOT_A.equals(binaryExpression.getOperator()) || OperatorKind.GREATER_THAN.equals(binaryExpression.getOperator()) || OperatorKind.GREATER_THAN_OR_EQUAL.equals(binaryExpression.getOperator()) || OperatorKind.LESS_THAN.equals(binaryExpression.getOperator()) || OperatorKind.LESS_THAN_OR_EQUAL.equals(binaryExpression.getOperator())) { processComparisonExpression(sb, binaryExpression, elementMap, stats); } else { throw new InternalErrorException(new Exception("Unknown operator '" + binaryExpression.getOperator() + "'")); } } protected void processUnaryExpression(StringBuffer sb, UnaryExpression unaryExpression, Map<String, ArchetypeReference> archetypeReferenceMap, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { if (OperatorKind.NOT.equals(unaryExpression.getOperator())) { sb.append("not("); processExpressionItem(sb, unaryExpression.getOperand(), archetypeReferenceMap, elementMap, stats); sb.append(")"); } else if (OperatorKind.FOR_ALL.equals(unaryExpression.getOperator())) { sb.append("forall("); processExpressionItem(sb, unaryExpression.getOperand(), archetypeReferenceMap,elementMap, stats); sb.append(")"); } else { throw new InternalErrorException(new Exception( "Unknown operator '" + unaryExpression.getOperator() + "'")); } } protected void processComparisonExpression(StringBuffer sb, BinaryExpression binaryExpression, Map<String, ArchetypeElementVO> elementMap, Map<RefStat, Set<String>> stats) throws InternalErrorException { Variable var = null; if (binaryExpression.getLeft() instanceof Variable) { var = (Variable) binaryExpression.getLeft(); stats.get(RefStat.REFERENCE).add(var.getCode()); } if (var != null) { if (var.getAttribute() == null) { if (binaryExpression.getRight() instanceof ConstantExpression) { ConstantExpression constantExpression = (ConstantExpression) binaryExpression.getRight(); String dvStr = constantExpression.getValue(); DataValue dv = null; if (!dvStr.equals("null")) { ArchetypeElementVO archetypeElementVO = elementMap.get(var.getCode()); if (archetypeElementVO==null){ throw new InternalErrorException(new Exception("Element '"+var.getCode()+"' not found. (guideId='"+guide.getId()+"')")); } String rmType = archetypeElementVO.getRMType(); dv = DataValue.parseValue(rmType + "," + dvStr); } if (dv != null) { sb.append("eval("); if (!OpenEHRConst.CURRENT_DATE_TIME_ID.equals(var.getCode())){ sb.append("$" + var.getCode() + ".hasValue() && "); } sb.append(getOperatorMVELLine("$"+var.getCode(), binaryExpression.getOperator(), DVDefSerializer.getDVInstantiation(dv))); sb.append(")"); } else { if (OperatorKind.EQUALITY.equals(binaryExpression .getOperator())) { sb.append("eval($" + var.getCode()+ ".hasNoValue(\""+guide.getId()+"/"+var.getCode()+"\"))"); } else if (OperatorKind.INEQUAL.equals(binaryExpression .getOperator())) { sb.append("eval($" + var.getCode() + ".hasValue())"); } } } else if (binaryExpression.getRight() instanceof Variable) { Variable varRight = (Variable) binaryExpression.getRight(); String gtCodeAux = varRight.getCode(); sb.append("eval($" + var.getCode() + ".hasValue() && "); sb.append("$" + gtCodeAux + ".hasValue() && "); sb.append(getOperatorMVELLine("$"+var.getCode(), binaryExpression.getOperator(), "$" + gtCodeAux)); sb.append(")"); stats.get(RefStat.REFERENCE).add(gtCodeAux); } else { throw new InternalErrorException(new Exception( "Unknown expression '" + binaryExpression.getRight().getClass() .getName() + "'")); } } else { if (var.getAttribute().equals(OpenEHRConst.NULL_FLAVOR_ATTRIBUTE)){ ConstantExpression constantExpression = (ConstantExpression) binaryExpression.getRight(); String dvStr = constantExpression.getValue(); DataValue dv = DataValue.parseValue(OpenEHRDataValues.DV_CODED_TEXT + "," + dvStr); sb.append("eval("); String opNeg = (binaryExpression.getOperator().equals(OperatorKind.INEQUAL))?"!":""; sb.append(opNeg+"DVUtil.nullValueEquals($"+var.getCode()+".getNullFlavour(), "+DVDefSerializer.getDVInstantiation(dv)+"))"); }else{//Expression Map<RefStat, Set<String>> statsAux = initStats(); String artimeticExpStr = ExpressionUtil.getArithmeticExpressionStr(elementMap, binaryExpression.getRight(), statsAux); //Add stats ExpressionUtil.getArithmeticExpressionStr(elementMap, binaryExpression.getLeft(), statsAux); stats.get(RefStat.REFERENCE).addAll(statsAux.get(RefStat.REFERENCE)); stats.get(RefStat.ATT_FUNCTIONS).addAll(statsAux.get(RefStat.ATT_FUNCTIONS)); statsAux.get(RefStat.REFERENCE).remove(OpenEHRConst.CURRENT_DATE_TIME_ID); sb.append("eval("); for (String gtCode : statsAux.get(RefStat.REFERENCE)) { sb.append("$" + gtCode + ".hasValue() && "); } String rmName = elementMap.get(var.getCode()).getRMType(); sb.append("("); String varCall = ExpressionUtil.getVariableWithAttributeStr(rmName, var); if (isString(rmName, var.getAttribute())){ sb.append(getAttributeOperatorMVELLine(varCall, binaryExpression.getOperator(), artimeticExpStr)); }else{ sb.append(varCall); sb.append(binaryExpression.getOperator().getSymbol()); sb.append(artimeticExpStr); } sb.append("))"); } } } else { throw new InternalErrorException(new Exception("Unknown expression '" + binaryExpression.getLeft() + "'")); } } private String getHasValueStr(Collection<String> gtCodes) { if (!gtCodes.isEmpty()) { StringBuffer sb = new StringBuffer(); sb.append(" eval("); int count = 0; for (String gtCode : gtCodes) { sb.append("$" + gtCode + ".hasValue()"); if (++count < gtCodes.size()) { sb.append(" && "); } } sb.append(")\n"); return sb.toString(); } else { return null; } } private String getFunctionsExtraCode(Collection<String> gtCodesWithFunctions){ StringBuffer sb = new StringBuffer(); for (String gtCodesWithFunction : gtCodesWithFunctions) { String[] codeSplit = gtCodesWithFunction.split(ExpressionUtil.CODE_FUNCTION_SEPARATOR); String code = codeSplit[0]; String att = codeSplit[1]; if (ExpressionUtil.isFunction(att)){ if (OpenEHRDataValues.FUNCTION_COUNT.equals(att)){ String elementId = _gtElementToElementId.get(code); //String def = "ElementInstance(id==\""+elementId+"\", dataValue!=null)"; /*TODO HACK - Should be done in a proper way...*/ String definition = _gtElementToWholeDefinition.get(code); String defAux = definition .replace("\n"," and\n"+TAB) .replace("$","$count_") .replace("eval(DVUtil.equalDV(true, $count_predicate", "eval(DVUtil.equalDV(false, $count_predicate") .replace("eval(DVUtil.isSubClassOf(true, $count_predicate", "eval(DVUtil.isSubClassOf(false, $count_predicate") .replace("$count_"+code+":ElementInstance(", "$count_"+code+":ElementInstance(!predicate, dataValue!=null, ") .replace("$count_"+OpenEHRConst.CURRENT_DATE_TIME_ID,"$"+OpenEHRConst.CURRENT_DATE_TIME_ID); if (defAux.length()>5){ //Remove last ' and\n'+TAB defAux = defAux.substring(0, defAux.length()-5-TAB.length()); } sb.append(TAB); sb.append("Number($"+code+att+":intValue) from accumulate (\n"+TAB+defAux+",\n"+TAB+TAB+"count($count_"+code+"))\n"); } } } String str = sb.toString(); return str.isEmpty()?null:str; } private String getOperatorMVELLine(String handle, OperatorKind ok, String value) { return getOperatorMVELLine(handle, ok, value, false); } private String getOperatorMVELLine( String handle, OperatorKind ok, String value, boolean inPredicate) { if (OperatorKind.EQUALITY.equals(ok)) { return getEqualsString(handle, value, inPredicate, false); } else if (OperatorKind.INEQUAL.equals(ok)) { return getEqualsString(handle, value, inPredicate, true); } else if (OperatorKind.IS_A.equals(ok)) { return "DVUtil.isSubClassOf("+ inPredicate+", "+ handle + ", $bindingMap, "+ getTermBindings(value) + ")"; } else if (OperatorKind.IS_NOT_A.equals(ok)) { return "DVUtil.isNotSubClassOf(" + inPredicate + ", "+ handle+", $bindingMap, "+ getTermBindings(value) + ")"; } else if (OperatorKind.GREATER_THAN.equals(ok)) { return getComparisonString(handle, value) + ">0"; } else if (OperatorKind.GREATER_THAN_OR_EQUAL.equals(ok)) { return getComparisonString(handle, value) + ">=0"; } else if (OperatorKind.LESS_THAN.equals(ok)) { return getComparisonString(handle, value) + "<0"; } else if (OperatorKind.LESS_THAN_OR_EQUAL.equals(ok)) { return getComparisonString(handle, value) + "<=0"; } else { return null; } } private static String getEqualsString(String handle, String value, boolean inPredicate, boolean negated){ StringBuffer sb = new StringBuffer(); sb.append("DVUtil.equalDV("+inPredicate+", "+handle+"," + value); sb.append(getDataValueStrIfNeeded(value)+", "+negated+")"); return sb.toString(); } private static String getComparisonString(String handle, String value){ StringBuffer sb = new StringBuffer(); sb.append("DVUtil.compatibleComparison(" + handle +getDataValueStrIfNeeded(handle)+ ", $auxDV="+ value+getDataValueStrIfNeeded(value) + ") && "); sb.append("DVUtil.compareDVs("+handle+".getDataValue(), $auxDV)"); return sb.toString(); } private static String getDataValueStrIfNeeded(String value){ if (value.startsWith("$")){ return ".getDataValue()"; }else{ return ""; } } private String getAttributeOperatorMVELLine( String handle, OperatorKind ok, String value) { if (OperatorKind.EQUALITY.equals(ok)) { return handle + ".equals("+ value + ")"; } else if (OperatorKind.INEQUAL.equals(ok)) { return "!" + handle + ".equals(" + value + ")"; } else { Logger.getLogger(GDLDroolsConverter.class).warn("Guide="+guide.getId()+", Illegar operator '"+ok.getSymbol()+"' used in handle '"+handle+"'."); return "false"; } } /* * Parse code from string value and generate right * code_phrase array for subClass evaluation * * possible values: * 1. new DvCodedText("Dubois and Dubois","local","gt0008") * 2. new DvText("local::gt0100") * 2. new DvText("local::gt0100|Hypertension|") */ protected String parseCode(String value) { int i = value.indexOf("local"); log.debug("value after IS_A: " + value); if (i < 0) { return value; } String code; if(value.contains("DvCodedText")) { code = value.substring(i + 8, value.length() - 2); } else if(value.contains("'")) { // due to a logic somewhere in gdl-editor introducing single quotation to code_phrase code = value.substring(i + 7, value.length() - 3); } else { code = value.substring(i + 7, value.length() - 2); } int j = code.indexOf("|"); if(j > 0) { code = code.substring(0, j); } log.debug("code parsed from value: " + code); return code; } private String getTermBindings(String value) { if (value.startsWith("$")){ Logger.getLogger(GDLDroolsConverter.class).warn("Guide="+guide.getId()+", Subclass comparison between elements is not supported."); //TODO Give support to subclass comparison between elements return "null"; } Map<String, TermBinding> termBindings = guide.getOntology().getTermBindings(); // TODO log.warn if gt code is unbound to terminologies if(termBindings == null) { //Logger.getLogger(GDLDroolsConverter.class).warn("Guide="+guide.getId()+", Needed terminology binding not found on guide."); return value; } String code = parseCode(value); StringBuffer buf = new StringBuffer("new DvCodedText[] {"); boolean first = true; for(String terminology : termBindings.keySet()) { log.debug("terminology: " + terminology); TermBinding termBinding = termBindings.get(terminology); Map<String, Binding> bindings = termBinding.getBindings(); log.debug("bindings: " + bindings); if(bindings.containsKey(code)) { log.debug("hasCode: " + code); Binding binding = bindings.get(code); if(binding.getCodes() != null) { for(CodePhrase cp : binding.getCodes()) { if(first) { first = false; } else { buf.append(","); } buf.append("new DvCodedText(\"text\",\""); buf.append(terminology); buf.append("\",\""); buf.append(cp.getCodeString()); buf.append("\")"); } } } } buf.append("}"); return buf.toString(); } private boolean isString(String rmName, String attribute){ return (OpenEHRDataValues.DV_TEXT.equals(rmName) && OpenEHRDataValues.VALUE_ATT.equals(attribute)) || (OpenEHRDataValues.DV_CODED_TEXT.equals(rmName) && OpenEHRDataValues.VALUE_ATT.equals(attribute)) || OpenEHRDataValues.UNITS_ATT.equals(attribute) || OpenEHRDataValues.CODE_ATT.equals(attribute) || OpenEHRDataValues.TEMINOLOGYID_ATT.equals(attribute); } private String getGuideHeader() { return "package se.cambio.cds;\n" + "import se.cambio.cds.model.instance.ArchetypeReference;\n" + "import se.cambio.cds.model.instance.ElementInstance;\n" + "import se.cambio.cds.model.instance.ContainerInstance;\n" + "import se.cambio.cds.util.DVUtil;\n" + "import org.openehr.rm.datatypes.quantity.DvOrdered;\n" + "import org.openehr.rm.datatypes.quantity.DvCount;\n" + "import org.openehr.rm.datatypes.quantity.DvOrdinal;\n" + "import org.openehr.rm.datatypes.quantity.DvQuantity;\n" + "import org.openehr.rm.datatypes.quantity.datetime.DvDate;\n" + "import org.openehr.rm.datatypes.quantity.datetime.DvDateTime;\n" + "import org.openehr.rm.datatypes.quantity.datetime.DvDuration;\n" + "import org.openehr.rm.datatypes.quantity.datetime.DvTime;\n" + "import org.openehr.rm.datatypes.quantity.DvProportion;\n" + "import org.openehr.rm.datatypes.quantity.ProportionKind;\n" + "import org.openehr.rm.datatypes.basic.DvBoolean;\n" + "import org.openehr.rm.datatypes.text.DvCodedText;\n" + "import org.openehr.rm.datatypes.text.DvText;\n" + "global se.cambio.cds.util.ExecutionLogger $executionLogger;\n" + "global org.openehr.rm.datatypes.basic.DataValue $auxDV;\n" + "global org.openehr.rm.datatypes.quantity.datetime.DvDateTime $"+OpenEHRConst.CURRENT_DATE_TIME_ID + ";\n" + "global java.util.Map<se.cambio.cds.model.instance.ElementInstance, java.util.Map<String, Boolean>> $bindingMap;\n" + "global java.lang.Integer "+DroolsExecutionManager.getGuideSalienceId(guide.getId())+";\n" + "\n"; } } /* * ***** 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 ***** */