package se.cambio.openehr.controller; import org.apache.log4j.Logger; import org.openehr.jaxb.am.*; import org.openehr.jaxb.rm.CodePhrase; import org.openehr.jaxb.rm.DvOrdinal; import org.openehr.jaxb.rm.TranslationDetails; import se.cambio.cm.model.archetype.vo.*; import se.cambio.openehr.controller.session.data.ArchetypeManager; import se.cambio.openehr.util.ArchetypeTermMapGenerator; import se.cambio.openehr.util.OpenEHRConst; import se.cambio.openehr.util.OpenEHRDataValues; import se.cambio.openehr.util.OpenEHRRMUtil; import se.cambio.openehr.util.UserConfigurationManager; import se.cambio.openehr.util.exceptions.ArchetypeProcessingException; import se.cambio.openehr.util.exceptions.InstanceNotFoundException; import se.cambio.openehr.util.exceptions.InternalErrorException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class GenericObjectBundleADLSManager { private final ArchetypeManager archetypeManager; private String language = null; private FlatArchetype ar = null; private Collection<ArchetypeElementVO> archetypeElementVOs; private Collection<ClusterVO> clusterVOs; private Collection<CodedTextVO> codedTextVOs; private Collection<OrdinalVO> ordinalVOs; private Collection<UnitVO> unitVOs; private Collection<ProportionTypeVO> proportionTypeVOs; private Map<String, Map<String, String>> termDefinitionsArchetypeTermMap; private Logger logger = Logger.getLogger(GenericObjectBundleADLSManager.class); private Map<String, List<String>> valueSetsMap; public GenericObjectBundleADLSManager(FlatArchetype ar, ArchetypeManager archetypeManager){ this.ar = ar; this.archetypeManager = archetypeManager; } public ArchetypeObjectBundleCustomVO generateObjectBundleCustomVO() throws ArchetypeProcessingException { init(); setDefaultLanguage(); loadArchetypeObjects(); return new ArchetypeObjectBundleCustomVO( archetypeElementVOs, clusterVOs, codedTextVOs, ordinalVOs, unitVOs, proportionTypeVOs); } private void setDefaultLanguage() { language = UserConfigurationManager.getLanguage(); if (!ar.getOriginalLanguage().getCodeString().equals(language) && (ar.getTranslations() == null || !containsLanguage(ar.getTranslations(),language))){ language = ar.getOriginalLanguage().getCodeString(); } } private boolean containsLanguage(List<TranslationDetails> translationDetails, String language){ for (TranslationDetails translationDetail: translationDetails){ String translationLanguage = translationDetail.getLanguage().getCodeString(); if (language.equals(translationLanguage)){ return true; } } return false; } private void init() { archetypeElementVOs = new ArrayList<ArchetypeElementVO>(); clusterVOs = new ArrayList<ClusterVO>(); codedTextVOs = new ArrayList<CodedTextVO>(); ordinalVOs = new ArrayList<OrdinalVO>(); unitVOs = new ArrayList<UnitVO>(); proportionTypeVOs = new ArrayList<ProportionTypeVO>(); } public void loadArchetypeObjects() throws ArchetypeProcessingException { String archId = getArchetypeId(); String rmEntry = ar.getDefinition().getRmTypeName(); String path = ""; for(CAttribute cAttribute: ar.getDefinition().getAttributes()){ processAttribute(cAttribute, path); } Collection<ArchetypeElementVO> rmArchetypeElements = OpenEHRRMUtil.getRMElements(archId, null, rmEntry); for (ClusterVO clusterVO: clusterVOs){ if (OpenEHRConst.isEntry(clusterVO.getRMType()) && !clusterVO.getPath().equals("/")){ rmArchetypeElements.addAll(OpenEHRRMUtil.getRMElements(archId, null, clusterVO.getRMType(), clusterVO.getPath())); } } archetypeElementVOs.addAll(rmArchetypeElements); } private String getArchetypeId() { return ar.getArchetypeId().getValue(); } private void processCObject(CObject cObject, String path) throws ArchetypeProcessingException { if (cObject instanceof CComplexObject){ processCComplexObject((CComplexObject) cObject, path); } } private void processCComplexObject(CComplexObject cComplexObject, String path) throws ArchetypeProcessingException { List<CAttribute> cAttributes = cComplexObject.getAttributes(); String currentPath = getCurrentPath(cComplexObject, path); if (cComplexObject.getRmTypeName().equals("ELEMENT")) { processElement(cComplexObject, currentPath); } else if (cComplexObject instanceof CArchetypeRoot){ String usedArchetypeId = ((CArchetypeRoot)cComplexObject).getArchetypeRef(); processArchetypeReference(usedArchetypeId, currentPath); } else { for(CAttribute cAttribute: cAttributes){ processAttribute(cAttribute, currentPath); } processClusters(cComplexObject, currentPath); } } private void processArchetypeReference(String usedArchetypeId, String path) throws ArchetypeProcessingException { String currentPath = path + "[" + usedArchetypeId + "]"; try { ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO = archetypeManager.getArchetypes().getArchetypeAOBCVOById(usedArchetypeId); processReferencedArcehtypeElements(currentPath, archetypeObjectBundleCustomVO); processReferencedClusters(currentPath, archetypeObjectBundleCustomVO); processReferencedCodedTexts(currentPath, archetypeObjectBundleCustomVO); processReferencedOrdinals(currentPath, archetypeObjectBundleCustomVO); processReferencedUnits(archetypeObjectBundleCustomVO); processReferencedProportionTypes(archetypeObjectBundleCustomVO); } catch (InternalErrorException e) { throw new ArchetypeProcessingException(e.getMessage()); } catch (InstanceNotFoundException e) { throw new ArchetypeProcessingException(e.getMessage()); } } private void processReferencedProportionTypes(ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(ProportionTypeVO proportionTypeVO: archetypeObjectBundleCustomVO.getProportionTypes()){ ProportionTypeVO proportionTypeVO1 = proportionTypeVO.clone(); String elementId = proportionTypeVO1.getIdElement(); int indexOfFirstSlash = elementId.indexOf("/"); String elementPath = elementId.substring(indexOfFirstSlash, elementId.length()); elementId = getArchetypeId() + elementPath; proportionTypeVO1.setIdElement(elementId); proportionTypeVOs.add(proportionTypeVO1); } } private void processReferencedUnits(ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(UnitVO unitVO: archetypeObjectBundleCustomVO.getUnitVOs()){ UnitVO unitVO1 = unitVO.clone(); String elementId = unitVO.getIdElement(); int indexOfFirstSlash = elementId.indexOf("/"); String elementPath = elementId.substring(indexOfFirstSlash, elementId.length()); elementId = getArchetypeId() + elementPath; unitVO1.setIdElement(elementId); unitVOs.add(unitVO1); } } private void processReferencedOrdinals(String currentPath, ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(OrdinalVO ordinalVO: archetypeObjectBundleCustomVO.getOrdinalVOs()){ OrdinalVO ordinalVO1 = ordinalVO.clone(); ordinalVO1.setIdArchetype(getArchetypeId()); ordinalVO1.setPath(currentPath + ordinalVO.getPath()); ordinalVOs.add(ordinalVO1); } } private void processReferencedCodedTexts(String currentPath, ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(CodedTextVO codedTextVO: archetypeObjectBundleCustomVO.getCodedTextVOs()){ CodedTextVO codedTextVO1 = codedTextVO.clone(); codedTextVO1.setIdArchetype(getArchetypeId()); codedTextVO1.setPath(currentPath + codedTextVO.getPath()); codedTextVOs.add(codedTextVO1); } } private void processReferencedClusters(String currentPath, ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(ClusterVO clusterVO: archetypeObjectBundleCustomVO.getClusterVOs()){ ClusterVO clusterVO1 = clusterVO.clone(); clusterVO1.setIdArchetype(getArchetypeId()); clusterVO1.setPath(currentPath + clusterVO.getPath()); clusterVOs.add(clusterVO1); } } private void processReferencedArcehtypeElements(String currentPath, ArchetypeObjectBundleCustomVO archetypeObjectBundleCustomVO) { for(ArchetypeElementVO archetypeElementVO: archetypeObjectBundleCustomVO.getArchetypeElementVOs()){ ArchetypeElementVO archetypeElementVO1 = archetypeElementVO.clone(); archetypeElementVO1.setIdArchetype(getArchetypeId()); archetypeElementVO1.setPath(currentPath + archetypeElementVO.getPath()); archetypeElementVOs.add(archetypeElementVO1); } } private String getCurrentPath(CComplexObject cComplexObject, String path) { if (cComplexObject.getNodeId() != null) { return path + "[" + cComplexObject.getNodeId() + "]"; } else { return path; } } private void processElement(CComplexObject cComplexObject, String currentPath) throws ArchetypeProcessingException { if (hasCardinalityZero(cComplexObject)){ return; } String nodeId = cComplexObject.getNodeId(); Map<String, String> termMap = getTermDefinitionsArchetypeTermMap().get(nodeId); if (termMap == null) { logger.warn("Archetype term not found for atCode '" + nodeId + "'"); return; } String text = getTermDefinitionsArchetypeTermMap().get(nodeId).get("text"); String description = termMap.get("description"); CObject elementDefinitionCObject = getElementDefinitionCObject(cComplexObject, currentPath, "value"); String rmType = elementDefinitionCObject.getRmTypeName(); rmType = translateCIMIRM(rmType); rmType = mapToKnowRMType(rmType); ArchetypeElementVO archetypeElementVO = new ArchetypeElementVOBuilder() .setName(text) .setDescription(description) .setIdArchetype(getArchetypeId()) .setType(rmType) .setPath(currentPath) .createArchetypeElementVO(); setCardinalities(archetypeElementVO, cComplexObject); archetypeElementVOs.add(archetypeElementVO); processAdditionalPathables(currentPath, elementDefinitionCObject, rmType); } private String mapToKnowRMType(String rmType) { if ("C_TERMINOLOGY_CODE".equals(rmType)){ return OpenEHRDataValues.DV_CODED_TEXT; } else { return rmType; } } private void processAdditionalPathables(String currentPath, CObject elementDefinitionCObject, String rmType) throws ArchetypeProcessingException { if (OpenEHRDataValues.DV_QUANTITY.equals(rmType)) { loadUnits(elementDefinitionCObject, currentPath); } else if (elementDefinitionCObject instanceof CTerminologyCode) { processCTerminologyCode((CTerminologyCode) elementDefinitionCObject, currentPath); } else if (OpenEHRDataValues.DV_CODED_TEXT.equals(rmType)) { processCodedTexts(elementDefinitionCObject, currentPath); } else if (OpenEHRDataValues.DV_ORDINAL.equals(rmType)) { processOrdinals(elementDefinitionCObject, currentPath); } else if (OpenEHRDataValues.DV_PROPORTION.equals(rmType)) { processProprotion(elementDefinitionCObject, currentPath); } } private void processCTerminologyCode(CTerminologyCode cTerminologyCode, String currentPath) throws ArchetypeProcessingException { List<String> acCodes = cTerminologyCode.getCodeList(); for (String acCode: acCodes) { List<String> members = getValueSetsMap().get(acCode); if (members == null) { logger.warn("No members could be found for code '" + acCode + "'"); return; } for (String memberCode : members) { processCodedTextItem("local", currentPath, memberCode); } } } private void processCodedTexts(CObject cObject, String currentPath) throws ArchetypeProcessingException { if (cObject instanceof CComplexObject){ CComplexObject cComplexObject = (CComplexObject) cObject; CObject cObjectDefinedCode = getElementDefinitionCObject(cComplexObject, currentPath, "defining_code"); if (cObjectDefinedCode instanceof CTerminologyCode) { CTerminologyCode cTerminologyCode = (CTerminologyCode) cObjectDefinedCode; processCTerminologyCode(cTerminologyCode, currentPath); } else { processCodedTextsWithoutDefiningCode(cComplexObject, currentPath); } } } private void processCodedTextsWithoutDefiningCode(CComplexObject cComplexObject, String currentPath) throws ArchetypeProcessingException { Map<String, List<String>> terminologyCodesMap = getTerminologyCodesMap(cComplexObject, currentPath); for(Map.Entry<String, List<String>> entrySet: terminologyCodesMap.entrySet()){ for(String code: entrySet.getValue()) { processCodedTextItem(entrySet.getKey(), currentPath, code); } } } private Map<String, List<String>> getTerminologyCodesMap(CComplexObject cComplexObject, String currentPath) throws ArchetypeProcessingException { Map<String, List<String>> terminologyCodesMap = new HashMap<String, List<String>>(); CObject terminologyIdCObject = getElementDefinitionCObject(cComplexObject, currentPath, "terminology_id"); CObject termIdCObject = getElementDefinitionCObject(cComplexObject, currentPath, "term_id"); if (terminologyIdCObject instanceof CComplexObject && termIdCObject instanceof CString) { CObject terminologyIdCString = getElementDefinitionCObject((CComplexObject) terminologyIdCObject, currentPath, "value"); String terminologyId = null; if (terminologyIdCString instanceof CString) { terminologyId = ((CString) terminologyIdCString).getDefaultValue(); } List<String> codes = ((CString) termIdCObject).getList(); terminologyCodesMap.put(terminologyId, codes); } return terminologyCodesMap; } private void processCodedTextItem(String terminologyId, String currentPath, String code) throws ArchetypeProcessingException { Map<String, String> termMap = getTermDefinitionsArchetypeTermMap().get(code); if (termMap == null) { logger.warn("Archetype term not found for atCode '" + code + "'"); return; } CodedTextVO codedTextVO = new CodedTextVOBuilder() .setName(termMap.get("text")) .setDescription(termMap.get("description")) .setType(OpenEHRDataValues.DV_CODED_TEXT) .setIdArchetype(getArchetypeId()) .setCode(code) .setTerminology(terminologyId) .setPath(currentPath) .createCodedTextVO(); codedTextVOs.add(codedTextVO); } private void processOrdinals(CObject cObject, String currentPath) throws ArchetypeProcessingException { if (cObject instanceof CComplexObject){ CObject cObjectDefinedCode = getElementDefinitionCObject((CComplexObject)cObject, currentPath, "value"); if (cObjectDefinedCode instanceof CDvOrdinal) { CDvOrdinal cDvOrdinal = (CDvOrdinal) cObjectDefinedCode; for (DvOrdinal dvOrdinal: cDvOrdinal.getList()) { CodePhrase definingCode = dvOrdinal.getSymbol().getDefiningCode(); String code = definingCode.getCodeString(); Map<String, String> referenceTerm = getTermDefinitionsArchetypeTermMap().get(code); if (referenceTerm == null) { logger.warn("Archetype term not found for atCode '" + code + "'"); continue; } OrdinalVO ordinalVO = new OrdinalVOBuilder() .setName(referenceTerm.get("text")) .setDescription(referenceTerm.get("description")) .setType(cObject.getRmTypeName()) .setIdArchetype(getArchetypeId()) .setValue(dvOrdinal.getValue()) .setCode(code) .setTerminology(definingCode.getTerminologyId().getValue()) .setPath(currentPath) .createOrdinalVO(); ordinalVOs.add(ordinalVO); } } } } private void processClusters(CObject cObject, String path) throws ArchetypeProcessingException { String nodeId = cObject.getNodeId(); Map<String, String> termMap = getTermDefinitionsArchetypeTermMap().get(nodeId); if (termMap == null) { logger.warn("Archetype term not found for atCode '" + nodeId + "'"); return; } ClusterVO clusterVO = new ClusterVOBuilder() .setName(termMap.get("text")) .setDescription(termMap.get("description")) .setType(cObject.getRmTypeName()) .setIdArchetype(getArchetypeId()) .setPath(path) .createClusterVO(); setCardinalities(clusterVO, cObject); clusterVOs.add(clusterVO); } private static void setCardinalities(PathableVO pathableVO, CObject cObject){ pathableVO.setLowerCardinality(cObject.getOccurrences().getLower()); pathableVO.setUpperCardinality(cObject.getOccurrences().getUpper()); } private void loadUnits(CObject cObject, String path) throws ArchetypeProcessingException { if (cObject instanceof CComplexObject){ CComplexObject cComplexObject = (CComplexObject) cObject; List<CAttributeTuple> attributeTuples = cComplexObject.getAttributeTuples(); for(CAttributeTuple cAttributeTuple: attributeTuples) { processUnits(path, cAttributeTuple); } CObject elementDefinitionCObject = getElementDefinitionCObject(cComplexObject, path, "units"); if (elementDefinitionCObject instanceof CTerminologyCode) { List<String> codes = ((CTerminologyCode) elementDefinitionCObject).getCodeList(); List<String> units = new ArrayList<String>(); for(String code: codes){ Map<String, String> termMap = getTermDefinitionsArchetypeTermMap().get(code); if (termMap == null){ throw new ArchetypeProcessingException("Unknown identifier '" + code + "'"); } units.add(termMap.get("text")); } String elementId = getArchetypeId() + path; processUnits(elementId, units); } else if (elementDefinitionCObject instanceof CComplexObject) { processUnitsWithoutCTerminologyCode((CComplexObject)elementDefinitionCObject, path); } } } private void processUnitsWithoutCTerminologyCode(CComplexObject cComplexObject, String path) throws ArchetypeProcessingException { Map<String, List<String>> terminologyCodesMap = getTerminologyCodesMap(cComplexObject, path); String elementId = getArchetypeId() + path; for(Map.Entry<String, List<String>> entrySet: terminologyCodesMap.entrySet()){ List<String> units = new ArrayList<String>(); for (String code: entrySet.getValue()) { Map<String, String> termMap = getTermDefinitionsArchetypeTermMap().get(code); if (termMap == null){ logger.warn("Archetype term not found for atCode '" + code + "'"); continue; } units.add(termMap.get("text")); } processUnits(elementId, units); } } private void processUnits(String path, CAttributeTuple cAttributeTuple) throws ArchetypeProcessingException { List<String> units = getUnitsCStrings(cAttributeTuple); String elementId = getArchetypeId() + path; processUnits(elementId, units); } private void processUnits( String elementId, List<String> units) { for (String unit: units){ UnitVO unitVO = new UnitVO(null, elementId, unit); unitVOs.add(unitVO); } } private void processProprotion(CObject cObject, String currentPath) throws ArchetypeProcessingException { if (cObject instanceof CComplexObject) { CObject elementDefinitionCObject = getElementDefinitionCObject((CComplexObject)cObject, currentPath, "type"); List<Integer> proportionTypes = getCIntegers(elementDefinitionCObject); for (Integer proportionType: proportionTypes) { proportionTypeVOs.add(new ProportionTypeVO(null, getArchetypeId() + currentPath, proportionType)); } } } private List<String> getUnitsCStrings(CAttributeTuple cAttributeTuple) throws ArchetypeProcessingException { int unitsIndex = getAttributeIndex(cAttributeTuple, "units"); List<CObject> cObjects = getCObjectsFromTuple(cAttributeTuple, unitsIndex); return getCStrings(cObjects); } private List<String> getCStrings(List<CObject> cObjects) throws ArchetypeProcessingException { List<String> cStrings = new ArrayList<String>(); for(CObject cObject: cObjects){ if (!(cObject instanceof CString)) { throw new ArchetypeProcessingException("Expecting C_STRING but found '" + cObject.getRmTypeName() + "'"); } cStrings.addAll(((CString)cObject).getList()); } return cStrings; } private List<Integer> getCIntegers(CObject cObject) throws ArchetypeProcessingException { List<Integer> cIntegers = new ArrayList<Integer>(); if (!(cObject instanceof CInteger)) { throw new ArchetypeProcessingException("Expecting C_INTEGER but found '" + cObject.getRmTypeName() + "'"); } cIntegers.addAll(((CInteger) cObject).getList()); return cIntegers; } private List<CObject> getCObjectsFromTuple(CAttributeTuple cAttributeTuple, int index) { List<CObject> cObjects = new ArrayList<CObject>(); for (CObjectTuple cObjectTuple: cAttributeTuple.getChildren()){ cObjects.add(cObjectTuple.getMembers().get(index)); } return cObjects; } private Integer getAttributeIndex(CAttributeTuple cAttributeTuple, String attributeRMName) { Integer i = 0; for(CAttribute cSingleAttribute: cAttributeTuple.getMembers()){ if (cSingleAttribute.getRmAttributeName().equals(attributeRMName)){ return i; } i++; } return null; } private CObject getElementDefinitionCObject(CComplexObject cComplexObject, String path, String rmName) throws ArchetypeProcessingException { for(CAttribute cAttribute: cComplexObject.getAttributes()) { if (cAttribute.getRmAttributeName().equals(rmName)) { if (cAttribute.getChildren().isEmpty()) { throw new ArchetypeProcessingException("Could not find cComplexObject for element at path '" + path + "'"); } return cAttribute.getChildren().iterator().next(); } } return null; } private void processAttribute(CAttribute cAttribute, String path) throws ArchetypeProcessingException { for(CObject cObject: cAttribute.getChildren()) { processCObject(cObject, path + "/" + cAttribute.getRmAttributeName()); } } private Map<String, Map<String, String>> getTermDefinitionsArchetypeTermMap() throws ArchetypeProcessingException { if (termDefinitionsArchetypeTermMap == null) { ArchetypeTermMapGenerator archetypeTermMapGenerator = new ArchetypeTermMapGenerator(ar.getOntology().getTermDefinitions(), language); termDefinitionsArchetypeTermMap = archetypeTermMapGenerator.generateTermDefinitionsArchetypeTermMap(); } return termDefinitionsArchetypeTermMap; } private Map<String, List<String>> getValueSetsMap(){ if (valueSetsMap == null) { valueSetsMap = generateValueSetsMap(); } return valueSetsMap; } private Map<String, List<String>> generateValueSetsMap() { Map valueSetMap = new HashMap<String, List<String>>(); for(ValueSetItem valueSetItem : ar.getOntology().getValueSets()){ valueSetMap.put(valueSetItem.getId(), valueSetItem.getMembers()); } return valueSetMap; } private boolean hasCardinalityZero(CComplexObject cComplexObject) { return (cComplexObject.getOccurrences() != null) && (cComplexObject.getOccurrences().getUpper() != null) && (cComplexObject.getOccurrences().getUpper() <= 0); } private String translateCIMIRM(String rmName){ if ("PLAIN_TEXT".equals(rmName) || "TEXT".equals(rmName)){ return OpenEHRDataValues.DV_TEXT; } else if ("CODED_TEXT".equals(rmName)){ return OpenEHRDataValues.DV_CODED_TEXT; } else if ("ORDINAL".equals(rmName)){ return OpenEHRDataValues.DV_ORDINAL; } else if ("QUANTITY".equals(rmName)){ return OpenEHRDataValues.DV_QUANTITY; } else if ("YESNO".equals(rmName)){ return OpenEHRDataValues.DV_BOOLEAN; } else if ("COUNT".equals(rmName)){ return OpenEHRDataValues.DV_COUNT; } else if ("DURATION".equals(rmName)){ return OpenEHRDataValues.DV_DURATION; } else if ("DATE_TIME".equals(rmName)){ return OpenEHRDataValues.DV_DATE_TIME; } else if ("DATE".equals(rmName)){ return OpenEHRDataValues.DV_DATE; } else if ("TIME".equals(rmName)){ return OpenEHRDataValues.DV_TIME; } else if ("PROPORTION".equals(rmName)){ return OpenEHRDataValues.DV_PROPORTION; } else if ("IDENTIFIER".equals(rmName)){ return OpenEHRDataValues.DV_IDENTIFIER; } else { return rmName; } } }