package se.cambio.openehr.controller; import org.apache.log4j.Logger; import org.openehr.am.archetype.Archetype; import org.openehr.am.archetype.constraintmodel.ArchetypeSlot; import org.openehr.am.archetype.constraintmodel.CAttribute; import org.openehr.am.archetype.constraintmodel.CComplexObject; import org.openehr.am.archetype.constraintmodel.CObject; import org.openehr.am.archetype.constraintmodel.CPrimitiveObject; import org.openehr.am.archetype.constraintmodel.primitive.CInteger; import org.openehr.am.archetype.constraintmodel.primitive.CPrimitive; import org.openehr.am.archetype.ontology.ArchetypeTerm; import org.openehr.am.openehrprofile.datatypes.quantity.CDvOrdinal; import org.openehr.am.openehrprofile.datatypes.quantity.CDvQuantity; import org.openehr.am.openehrprofile.datatypes.quantity.CDvQuantityItem; import org.openehr.am.openehrprofile.datatypes.quantity.Ordinal; import org.openehr.am.openehrprofile.datatypes.text.CCodePhrase; import org.openehr.rm.datatypes.text.CodePhrase; import org.openehr.rm.datatypes.text.DvCodedText; import se.cambio.cm.model.archetype.vo.*; import se.cambio.cm.model.facade.terminology.vo.TerminologyNodeVO; import se.cambio.openehr.controller.session.OpenEHRSessionManager; import se.cambio.openehr.util.ExceptionHandler; import se.cambio.openehr.util.OpenEHRConst; import se.cambio.openehr.util.OpenEHRDataValues; import se.cambio.openehr.util.OpenEHRDataValuesUI; import se.cambio.openehr.util.OpenEHRRMUtil; import se.cambio.openehr.util.UserConfigurationManager; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; public class GenericObjectBundleADLManager { private static String SECTION_NAME = "name/value='"; protected String templateId = null; private String language = null; private Archetype 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 final Map<String, Archetype> archetypeMap; public GenericObjectBundleADLManager(Archetype ar, Map<String, Archetype> archetypeMap){ this.ar = ar; this.archetypeMap = archetypeMap; } public GenericObjectBundleADLManager(Archetype ar, String templateId, Map<String, Archetype> archetypeMap) { this.ar = ar; this.templateId = templateId; this.archetypeMap = archetypeMap; } public ArchetypeObjectBundleCustomVO generateObjectBundleCustomVO(){ 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 || !ar.getTranslations().containsKey(language))){ language = ar.getOriginalLanguage().getCodeString(); } } 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(){ String archId = ar.getArchetypeId().getValue(); String rmEntry = ar.getArchetypeId().rmEntity(); proccessCObject(ar.getDefinition()); Collection<ArchetypeElementVO> rmArchetypeElements = OpenEHRRMUtil.getRMElements(archId, templateId, rmEntry); for (ClusterVO clusterVO: clusterVOs){ if (OpenEHRConst.isEntry(clusterVO.getRMType()) && !clusterVO.getPath().equals("/")){ rmArchetypeElements.addAll(OpenEHRRMUtil.getRMElements(archId, templateId, clusterVO.getRMType(), clusterVO.getPath())); } } archetypeElementVOs.addAll(rmArchetypeElements); } private void proccessCObject(CObject cObject){ String path = cObject.path(); String archetypeId = ar.getArchetypeId().getValue(); if (!OpenEHRConst.PARSABLE_OPENEHR_RM_NAMES.contains(cObject.getRmTypeName())){ return; } if (cObject instanceof CComplexObject){ processComplexObject(cObject, path, archetypeId); }else if (cObject instanceof ArchetypeSlot){ //Skip } } private void processComplexObject(CObject cObject, String path, String archetypeId) { CComplexObject cComplexObject = ((CComplexObject)cObject); CAttribute att = cComplexObject.getAttribute("value"); Archetype localAOM = getLocalAOM(ar, path); String text = null; String desc = null; text = getText(localAOM, cObject.getNodeId(), language); desc = getDescription(localAOM, cObject.getNodeId(), language); if (text == null){ text = cObject.getNodeId(); } if (desc == null){ desc = cObject.getNodeId(); } //TODO ???? if ("@ internal @".equals(desc) || text==null || text.startsWith("*")){ int firstIndex = path.lastIndexOf("/")+1; int finalIndex = path.lastIndexOf("["); if (finalIndex > firstIndex){ text = path.substring(firstIndex, finalIndex); } } CObject childCObject = getCChildCObject(att); String type = getType(cObject, att); if (hasCardinalityZero(cComplexObject)){ return; } if (OpenEHRDataValuesUI.isManaged(type)){ processDataValue(cObject, path, archetypeId, localAOM, text, desc, type, childCObject); }else{ processSectionsAndClusters(cObject, path, archetypeId, text, desc, type); } //Recursive lookup for (CAttribute cAttribute: cComplexObject.getAttributes()){ for (CObject cObjectAux : cAttribute.getChildren()){ proccessCObject(cObjectAux); } } } private String getType(CObject cObject, CAttribute att) { String type; if (att != null){ if (att.getChildren() != null && !att.getChildren().isEmpty()){ type = att.getChildren().get(0).getRmTypeName(); }else{ type = cObject.getRmTypeName(); } }else{ type = cObject.getRmTypeName(); } type = convertTypeIfQuantityOrOrdinal(type); return type; } private CObject getCChildCObject(CAttribute att) { CObject childCObject = null; if ((att != null) && (att.getChildren() != null) && !att.getChildren().isEmpty()) { childCObject = att.getChildren().get(0); } return childCObject; } private boolean hasCardinalityZero(CComplexObject cComplexObject) { return (cComplexObject.getOccurrences() != null) && (cComplexObject.getOccurrences().getUpper() != null) && (cComplexObject.getOccurrences().getUpper() <= 0); } private String convertTypeIfQuantityOrOrdinal(String type) { if (type != null){ if (type.equals("DvOrdinal")){ type = OpenEHRDataValues.DV_ORDINAL; }else if (type.equals("DvQuantity")){ type = OpenEHRDataValues.DV_QUANTITY; } } return type; } private void processSectionsAndClusters(CObject cObject, String path, String archetypeId, String text, String desc, String type) { if (type.equals(OpenEHRConst.SECTION)){ String auxText = getSectionName(path); //If the user specifies a special name on the template designer if (auxText != null){ text = auxText; } } ClusterVO clusterVO = new ClusterVOBuilder() .setName(text) .setDescription(desc) .setType(type) .setIdArchetype(archetypeId) .setIdTemplate(templateId) .setPath(path) .createClusterVO(); setCardinalities(clusterVO, cObject); clusterVOs.add(clusterVO); } private void processDataValue(CObject cObject, String path, String archetypeId, Archetype localAOM, String text, String desc, String type, CObject childCObject) { ArchetypeElementVO archetypeElementVO = new ArchetypeElementVOBuilder() .setName(text) .setDescription(desc) .setType(type) .setIdArchetype(archetypeId) .setIdTemplate(templateId) .setPath(path) .createArchetypeElementVO(); setCardinalities(archetypeElementVO, cObject); archetypeElementVOs.add(archetypeElementVO); if (OpenEHRDataValues.DV_CODED_TEXT.equals(type)){ if (codedTextVOs != null){ loadCodedTexts(archetypeId, localAOM, templateId, path, childCObject, archetypeElementVO.getId(), language, codedTextVOs); } }else if (OpenEHRDataValues.DV_ORDINAL.equals(type)) { if (ordinalVOs != null){ loadOrdinals(archetypeId, localAOM, templateId, path, childCObject, archetypeElementVO.getId(), language, ordinalVOs); } }else if (OpenEHRDataValues.DV_QUANTITY.equals(type)) { if (unitVOs != null){ loadUnits(templateId, archetypeElementVO.getId(), childCObject, unitVOs); } }else if (OpenEHRDataValues.DV_PROPORTION.equals(type)) { if (proportionTypeVOs != null){ loadProportionTypes(templateId, archetypeElementVO.getId(), childCObject, proportionTypeVOs); } } } private static void setCardinalities(PathableVO pathableVO, CObject cObject){ //TODO pathableVO.setLowerCardinality(cObject.getOccurrences().getLower()); pathableVO.setUpperCardinality(cObject.getOccurrences().getUpper()); } private static String getIdParentCluster(String path, Collection<ClusterVO> clusterVOs){ ArrayList<ClusterVO> parentClusters = new ArrayList<ClusterVO>(); for (ClusterVO clusterVO : clusterVOs) { if (path.startsWith(clusterVO.getPath())){ parentClusters.add(clusterVO); } } String idParentCluster = null; int length = 0; for (ClusterVO clusterVO : parentClusters) { if (clusterVO.getPath().length() > length){ idParentCluster = clusterVO.getId(); length = clusterVO.getPath().length(); } } return idParentCluster; } private static void loadCodedTexts( String archetypdId, Archetype ar, String templateId, String path, CObject childCObject, String idElement, String language, Collection<CodedTextVO> codedTextVOs){ boolean codedListFound = false; if (childCObject instanceof CComplexObject){ List<CAttribute> atts = ((CComplexObject)childCObject).getAttributes(); if (atts != null){ int i = 0; Iterator<CAttribute> atIt = atts.iterator(); while (atIt.hasNext() && !codedListFound){ CAttribute att2 = atIt.next(); List<CObject> childAtts = att2.getChildren(); Iterator<CObject> childattsIt = childAtts.iterator(); while (childattsIt.hasNext() && !codedListFound) { CObject cObject = childattsIt.next(); if (cObject instanceof CCodePhrase){ CCodePhrase cCodePhrase = ((CCodePhrase)cObject); //CodePhrase assumed = cCodePhrase.getAssumedValue(); if (cCodePhrase.getCodeList() != null){ for (String codedStr : cCodePhrase.getCodeList()) { String text = codedStr; String desc = codedStr; String terminologyId = cCodePhrase.getTerminologyId().getValue(); CodedTextVO codedText = new CodedTextVOBuilder() .setName(text) .setDescription(desc) .setType(cCodePhrase.getRmTypeName()) .setIdArchetype(archetypdId) .setIdTemplate(templateId) .setPath(path) .setTerminology(terminologyId) .setCode(codedStr) .createCodedTextVO(); if (terminologyId.equals(OpenEHRConst.LOCAL)){ codedText.setName(getText(ar, codedStr, language)); codedText.setDescription(getDescription(ar, codedStr, language)); }else{ addSubclassCodedTexts(codedText, codedTextVOs); } codedTextVOs.add(codedText); if (!"local".equals(terminologyId) && i++>15){ //No need to load the whole terminology for external references return; } }; codedListFound = true; } } } } } } } private static void addSubclassCodedTexts(CodedTextVO codedTextVO, Collection<CodedTextVO> codedTextVOs){ if (!OpenEHRConst.LOCAL.equals(codedTextVO.getTerminology())){ try { TerminologyNodeVO node = OpenEHRSessionManager.getTerminologyFacadeDelegate().retrieveAllSubclasses( new CodePhrase(codedTextVO.getTerminology(), codedTextVO.getCode()), OpenEHRDataValuesUI.getLanguageCodePhrase()); if (node == null){ Logger.getLogger(GenericObjectBundleADLManager.class).warn("Terminology code not found '"+codedTextVO.getCode()+"::"+codedTextVO.getTerminology()+"'."); return; } DvCodedText ct = node.getValue(); codedTextVO.setName(getValidCodedTextName(ct.getValue())); codedTextVO.setDescription(getValidCodedTextName(ct.getValue())); if (codedTextVOs.size()>15){ //No need to load the whole terminology return; } addCodedTextVOs(node, codedTextVO, codedTextVOs); } catch (Exception e){ ExceptionHandler.handle(e); } } } private static void addCodedTextVOs(TerminologyNodeVO root, CodedTextVO rootCodedTextVO, Collection<CodedTextVO> codedTextVOs){ if (codedTextVOs.size()>15){ //No need to load the whole terminology return; } for (TerminologyNodeVO node : root.getChildren()) { DvCodedText ct = node.getValue(); CodedTextVO codedTextVO = new CodedTextVOBuilder() .setName(getValidCodedTextName(ct.getValue())) .setDescription(getValidCodedTextName(ct.getValue())) .setType(OpenEHRDataValues.DV_CODED_TEXT) .setIdArchetype(rootCodedTextVO.getIdArchetype()) .setIdTemplate(rootCodedTextVO.getIdTemplate()) .setPath(rootCodedTextVO.getPath()) .setTerminology(ct.getDefiningCode().getTerminologyId().getValue()) .setCode(ct.getDefiningCode().getCodeString()) .createCodedTextVO(); codedTextVOs.add(codedTextVO); addCodedTextVOs(node, codedTextVO, codedTextVOs); } } /* Remove all parenthesis to avoid parsing problems */ private static String getValidCodedTextName(String string){ return string.replaceAll("\\(", "[").replaceAll("\\)", "\\]"); } private static void loadOrdinals( String archetypdId, Archetype ar, String templateId, String path, CObject childCObject, String idElement, String language, Collection<OrdinalVO> ordinalVOs){ if (childCObject instanceof CDvOrdinal){ CDvOrdinal cDvOrdinal = (CDvOrdinal)childCObject; if (cDvOrdinal.getList() != null){ for (Ordinal ordinal : cDvOrdinal.getList()) { String codedStr = ordinal.getSymbol().getCodeString(); String text = codedStr; String desc = codedStr; if (ordinal.getSymbol().getTerminologyId().getValue().equals("local")){ text = getText(ar, codedStr, language); desc = getDescription(ar, codedStr, language); }else{ Logger.getLogger(GenericObjectBundleADLManager.class).error("Unkown terminology: '"+ordinal.getSymbol().getTerminologyId().getValue()+"', skipping..."); //TODO TERMINOLOGY SERVICE } ordinalVOs.add( new OrdinalVOBuilder() .setName(text) .setDescription(desc) .setType(cDvOrdinal.getRmTypeName()) .setIdArchetype(archetypdId) .setIdTemplate(templateId) .setPath(path) .setValue(ordinal.getValue()) .setTerminology(ordinal.getSymbol().getTerminologyId().getValue()) .setCode(ordinal.getSymbol().getCodeString()) .createOrdinalVO()); }; } } } private static void loadUnits( String templateId, String idElement, CObject childCObject, Collection<UnitVO> unitVOs){ if (childCObject instanceof CDvQuantity){ CDvQuantity cDvQuantity = (CDvQuantity)childCObject; if (cDvQuantity.getList()!=null){ for (CDvQuantityItem cDvQuantityItem : cDvQuantity.getList()) { unitVOs.add(new UnitVO(templateId, idElement, cDvQuantityItem.getUnits())); }; } } } private static void loadProportionTypes( String templateId, String idElement, CObject childCObject, Collection<ProportionTypeVO> proportionTypeVOs){ if (childCObject instanceof CComplexObject){ CComplexObject cComplexObject = (CComplexObject)childCObject; CAttribute cAttribute = cComplexObject.getAttribute("type"); if (cAttribute!=null){ for (CObject cObject : cAttribute.getChildren()) { if (cObject instanceof CPrimitiveObject){ CPrimitive cPrimitive = ((CPrimitiveObject) cObject).getItem(); if (cPrimitive instanceof CInteger){ CInteger cInteger = (CInteger) cPrimitive; for (Integer proportionType : cInteger.getList()) { proportionTypeVOs.add(new ProportionTypeVO(templateId, idElement, proportionType)); } } } }; } } } private static String getText(Archetype ar, String codeStr, String language){ ArchetypeTerm term = getArchetypeTerm(ar, codeStr, language); if (term != null){ return term.getText(); }else{ return null; } } private static String getDescription(Archetype ar, String codeStr, String language){ ArchetypeTerm term = getArchetypeTerm(ar, codeStr, language); if (term != null){ return term.getDescription(); }else{ return null; } } private static ArchetypeTerm getArchetypeTerm(Archetype ar, String codeStr, String language){ ArchetypeTerm term = ar.getOntology().termDefinition(language, codeStr); if (term == null){ term = ar.getOntology().termDefinition(ar.getOriginalLanguage().getCodeString(), codeStr); } return term; } public Archetype getLocalAOM(Archetype currentAOM, String path){ int i = path.lastIndexOf("["); while(i > 0){ String idArchetype = path.substring(i+1, path.lastIndexOf("]")); if (idArchetype.contains(" ")){ idArchetype = idArchetype.split(" ")[0]; } Archetype archetype = null; if (idArchetype.startsWith("openEHR")) { //TODO remove dependency on openEHR archetypes archetype = archetypeMap.get(idArchetype); } if (archetype != null) { return archetype; } else { path = path.substring(0, i); i = path.lastIndexOf("["); } }; return currentAOM; } private static String getSectionName(String path){ int i1 = path.lastIndexOf(SECTION_NAME)+SECTION_NAME.length(); int i2 = path.indexOf("'",i1); if ((i1 > 0) && (i2 > 0)){ return path.substring(i1, i2); }else{ return null; } } }