Russell Bateman
November 2019
Model-driven Health Tools (MDHT) constitute a Java-based set of libraries by which it is possible to generate Clinical Document Architecture (CDA) documents of most every sort. I have written copious amounts of code that take intermediate XML statements to guide calls into this library, then generate documents that pass various standard validators. The library works well, but is occasionally very complicated in its interfaces.
Website: https://github.com/mdht/mdht.
Related links:
Also called "analysis." This is using the MDHT library API to read (ingest) a CDA (CCD, etc.) into a ClinicalDocument so that the resulting document-object model (DOM) is at your fingertips for whatever purpose you might have. For example, the MDHT library validation component uses this. If you're analyzing a CCD, Care Plan, QRDA I or III, etc., you can get this help too.
package com.windofkeltia.mdht; import java.io.ByteArrayInputStream; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.mdht.uml.cda.ClinicalDocument; import org.eclipse.mdht.uml.cda.Section; import org.eclipse.mdht.uml.cda.util.CDAUtil; import org.eclipse.mdht.uml.hl7.datatypes.ST; import com.windofkeltia.utilities.TestUtilities; public class MdhtAsParserTest { @Rule public TestName name = new TestName(); @After public void tearDown() { } @Before public void setUp() { TestUtilities.setUp( name ); } private static final boolean VERBOSE = TestUtilities.VERBOSE; @Test public void test() throws Exception { final String PATHNAME = TestUtilities.TEST_FODDER + "sample-1.ccd"; final String CONTENT = TestUtilities.getLinesInFile( PATHNAME ); ByteArrayInputStream inputStream = new ByteArrayInputStream( CONTENT.getBytes() ); ClinicalDocument document = CDAUtil.load( inputStream ); boolean once = false; for( Section section : document.getSections() ) { int entries = section.getEntries().size(); ST title = section.getTitle(); FeatureMap featureMap = title.getMixed(); FeatureMap.ValueListIterator< Object > iterator = featureMap.valueListIterator(); if( !once ) { System.out.println( "Sections found in " + PATHNAME + ":" ); once = true; } while( iterator.hasNext() ) { Object object = iterator.next(); if( VERBOSE ) System.out.println( " " + entries + " " + object.toString() ); } } } }
sample-1.ccd is such that the output from the code above is:
Sections found in src/test/resources/fodder/sample-1.ccd: 1 Encounters 2 Social History 1 Procedures 1 Vital Signs 1 Results 1 Allergies, Adverse Reactions, Alerts 1 Payers 2 Problems 2 Medications
Here's the general lay of the land in MDHT. You do something by making calls into MDHT. When you get the library JARS, you'll find examples of this in sample source code. You set up the general framework around this; see method getOutput().
package com.windofkeltia.mdht; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import static org.junit.Assert.assertTrue; import org.junit.After; import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runners.MethodSorters; import org.eclipse.mdht.uml.cda.Act; import org.eclipse.mdht.uml.cda.CDAFactory; import org.eclipse.mdht.uml.cda.EntryRelationship; import org.eclipse.mdht.uml.cda.Observation; import org.eclipse.mdht.uml.cda.Section; import org.eclipse.mdht.uml.cda.util.CDAUtil; import org.eclipse.mdht.uml.hl7.vocab.ActClassObservation; import org.eclipse.mdht.uml.hl7.vocab.x_ActMoodDocumentObservation; import org.eclipse.mdht.uml.hl7.vocab.x_ActRelationshipEntryRelationship; import org.openhealthtools.mdht.uml.cda.consol.ConsolFactory; import org.openhealthtools.mdht.uml.cda.consol.ContinuityOfCareDocument2; import com.windofkeltia.utilities.TestUtilities; @FixMethodOrder( MethodSorters.JVM ) public class ShowOffMdhtTest { @Rule public TestName name = new TestName(); @After public void tearDown() { } @Before public void setUp() { TestUtilities.setUp( name ); } private static final String[] TEST_EXPECTED = new String[] { // this is the output you'll see in the println() "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<ClinicalDocument xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:hl7-org:v3\" xsi:schemaLocation=\"urn:hl7-org:v3 infrastructure/cda/CDA_SDTC.xsd\">", " <realmCode code=\"US\"/>", " <templateId root=\"2.16.840.1.113883.10.20.22.1.2\" extension=\"2015-08-01\"/>", " <templateId root=\"2.16.840.1.113883.10.20.22.1.1\"/>", " <templateId root=\"2.16.840.1.113883.10.20.22.1.1\" extension=\"2015-08-01\"/>", " <code code=\"34133-9\" codeSystem=\"2.16.840.1.113883.6.1\" codeSystemName=\"LOINC\" displayName=\"Summarization of Episode Note\"/>", " <confidentialityCode codeSystem=\"2.16.840.1.113883.5.25\" codeSystemName=\"ConfidentialityCode\"/>", " <component>", " <structuredBody>", " <component>", " <section>", " <entry>", " <observation classCode=\"OBS\" moodCode=\"EVN\"/>", " </entry>", " </section>", " </component>", " </structuredBody>", " </component>", "</ClinicalDocument>" }; @Test public void test() throws Exception { Observation observation = CDAFactory.eINSTANCE.createObservation(); observation.setClassCode( ActClassObservation.OBS ); observation.setMoodCode( x_ActMoodDocumentObservation.EVN ); final String OUTPUT = getOutput( observation ); System.out.print( OUTPUT ); for( String expected : EXPECTED ) assertTrue( OUTPUT.contains( expected ) ); } private static String getOutput( Observation observation ) throws Exception { ContinuityOfCareDocument2 document = ConsolFactory.eINSTANCE.createContinuityOfCareDocument2().init(); Section section = CDAFactory.eINSTANCE.createSection(); section.addObservation( observation ); document.addSection( section ); OutputStream outputStream = new ByteArrayOutputStream(); CDAUtil.save( document, outputStream ); return outputStream.toString(); } }
On occasion, the method offered by Observation (or Organizer, probably by still other classes), addObservation(), will secretly wrap the new observation in an entry relationship. When it does this, there's no opportunity to control the latter's typeCode. You must reach in and perform surgery. Here's how.
Let's agree that we've just called something that creates an observation. We're adding a series of observations to a hierarchically superior observation we already have (or to an organizer, etc.) using addObservation( Observation newObservation ). We don't want it to come out in the output like this:
<entryRelationship> <!-- what MDHT wraps you with --> <observation classCode="OBS" moodCode="EVN"> <!-- new observation you generated--> ... <entryRelationship inversionInd="true" typeCode="SUBJ"> <observation classCode="OBS" moodCode="EVN"> ... </observation> </entryRelationship> <reference typeCode="REF"> <externalObservation ...> ... </externaObservation> </reference> </observation> </entryRelationship>
Instead, you want this:
<entryRelationship typeCode="SUBJ"> <observation classCode="OBS" moodCode="EVN"> <!-- new observation you generated--> etc. as above... </observation> </entryRelationship>
The magic happens in the highlighted line. It cannot happen until the call to addObservation() has already been made. This is because the eContainer() method has nothing to report until after the wrapping has occurred. The wrapping occurs a couple of methods deep below addObservation().
Observation newObservation = createNewObservationToAdd(); // say an Encounter, Problem, etc. observation.addObservation( newObservation ); // add the o EntryRelationship entryRelationship = ( EntryRelationship ) newObservation.eContainer(); entryRelationship.setTypeCode( x_ActRelationshipEntryRelationship.SUBJ );
Two examples contrasting—add this code to ShowOffMdhtTest above.
Also, we vet the output to ensure this is the case. The lines of code demanding our attention are highlighted.
private static final String[] TEST2_EXPECTED = new String[] { // this is the output you'll see in the println() "", " " }; @Test public void test2() throws Exception { Act act = CDAFactory.eINSTANCE.createAct(); Observation observation = CDAFactory.eINSTANCE.createObservation(); observation.setClassCode( ActClassObservation.OBS ); observation.setMoodCode( x_ActMoodDocumentObservation.EVN ); act.addObservation( observation ); final String OUTPUT = getOutput( act ); System.out.println( OUTPUT ); for( String expected : TEST2_EXPECTED ) assertTrue( OUTPUT.contains( expected ) ); } private static final String[] TEST2BIS_EXPECTED = new String[] { // this is the output you'll see in the println() "", " ", "", " ", "", " ", "", " ", "", " ", "", " ", "", " ", " " }; @Test public void test2bis() throws Exception { Act act = CDAFactory.eINSTANCE.createAct(); EntryRelationship entryRelationship = CDAFactory.eINSTANCE.createEntryRelationship(); Observation observation = CDAFactory.eINSTANCE.createObservation(); observation.setClassCode( ActClassObservation.OBS ); observation.setMoodCode( x_ActMoodDocumentObservation.EVN ); entryRelationship.setTypeCode( x_ActRelationshipEntryRelationship.SUBJ ); entryRelationship.setObservation( observation ); act.getEntryRelationships().add( entryRelationship ); final String OUTPUT = getOutput( act ); System.out.println( OUTPUT ); for( String expected : TEST2BIS_EXPECTED ) assertTrue( OUTPUT.contains( expected ) ); } . . . private static String getOutput( Act act ) throws Exception { ContinuityOfCareDocument2 document = ConsolFactory.eINSTANCE.createContinuityOfCareDocument2().init(); Section section = CDAFactory.eINSTANCE.createSection(); section.addAct( act ); document.addSection( section ); OutputStream outputStream = new ByteArrayOutputStream(); CDAUtil.save( document, outputStream ); return outputStream.toString(); }", " ", "", " ", "", " ", "", " ", "", " ", "", " ", "", "
This is fraught with considerable peril. For example, the following code will destroy document to the benefit of copy. By "destroy," I mean it will in effect "suck its guts out" and put them into copy.
import org.eclipse.mdht.uml.cda.CDAFactory; import org.eclipse.mdht.uml.cda.ClinicalDocument; public static ClinicalDocument makeShallowCopy( ClinicalDocument document ) { ClinicalDocument copy = CDAFactory.eINSTANCE.createClinicalDocument(); copy.unsetClassCode(); copy.unsetMoodCode(); copy.unsetNullFlavor(); copy.setTypeId ( document.getTypeId() ); copy.setId ( document.getId() ); copy.setCode ( document.getCode() ); copy.setTitle ( document.getTitle() ); copy.setEffectiveTime ( document.getEffectiveTime() ); copy.setConfidentialityCode ( document.getConfidentialityCode() ); copy.setLanguageCode ( document.getLanguageCode() ); copy.setId ( document.getSetId() ); copy.setVersionNumber ( document.getVersionNumber() ); copy.setClassCode ( document.getClassCode() ); copy.setMoodCode ( document.getMoodCode() ); copy.setNullFlavor ( document.getNullFlavor() ); copy.setDataEnterer ( document.getDataEnterer() ); copy.setCopyTime ( document.getCopyTime() ); copy.setCustodian ( document.getCustodian() ); copy.setLegalAuthenticator ( document.getLegalAuthenticator() ); copy.getRealmCodes().addAll ( document.getRealmCodes() ); copy.getTemplateIds().addAll ( document.getTemplateIds() ); copy.getRecordTargets().addAll ( document.getRecordTargets() ); copy.getAuthors().addAll ( document.getAuthors() ); copy.getAuthenticators().addAll ( document.getAuthenticators() ); copy.getAuthorizations().addAll ( document.getAuthorizations() ); copy.getInformants().addAll ( document.getInformants() ); copy.getInformationRecipients().addAll( document.getInformationRecipients() ); copy.getDocumentationOfs().addAll ( document.getDocumentationOfs() ); copy.getParticipants().addAll ( document.getParticipants() ); copy.getInFulfillmentOfs().addAll ( document.getInFulfillmentOfs() ); copy.getRelatedDocuments().addAll ( document.getRelatedDocuments() ); return copy; }
The only thing left in document after execution of the code above will be its top-level...
<component> <structuredBody> list of section components... </structuredBody> </component>
All (or most?) objects in the MDHT library inherit from base class Eobject (for ecore, it's an Eclipse thing). For this reason, it's possible to use EcoreUtil.Copier methods on them. Doing what these do, but by hand, would be a major nightmare.
It's easy to copy high-level or lower-level (subordinate) objects thus:
import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.mdht.uml.cda.ClinicalDocument; public static ClinicalDocument makeDeepCopy( ClinicalDocument document ) { EcoreUtil.Copier copier = new EcoreUtil.Copier(); EObject copy = copier.copy( document ); copier.copyReferences(); return ( ClinicalDocument ) copy; }
Once this is done, you can do anything you want to this (deep) copy with no repercussions for the original. If the original disappears from activation, the copy is still intact.
If you forget to do copier.copyReferences()
, you'll have what
looks like a good copy on the surface, but it will be missing important, deeper
data such as RecordTarget.PatientRole.Patient.names. In this precise
case, the name data structures get copied, but there is a field under
names (looking in the debugger here), mixed, that is null.
If you examine the original, you'll see that this is where the actual name
strings reside.
Numerous other data structures are like this. These are apparently the "references" you need to copy explicitly via this utility method.
Optimization: It appears that, once you've created a copier, you can reuse it. It's valid over more than the first use: For example, ...
import org.eclipse.emf.ecore.util.EcoreUtil; public void foo() { EcoreUtil.Copier copier = new EcoreUtil.Copier(); for( Section section : document.getSections() ) { Section sectionCopy = ( Section ) copier.copy( section ); copier.copyReferences(); // operate on the copy of the section made... ... for( Entry entry : section.getEntries() ) { Entry entryCopy = ( Entry ) copier.copy( entry ); copier.copyReferences(); // operate on the copy of the entry made... ... } }
However, beware of sullying data over the same range before copying it again and calling copyReference(). This could result in duplicates in some place such as address street lines, city names and country codes all repeating.
ClinicalDocument original = CDAUtil.load( new ByteArrayInputStream( CCD.getBytes() ) ); RecordTarget recordTarget = original.getRecordTargets().get( 0 ); PatientRole patientRole = ( PatientRole ) copier.copy( recordTarget.getPatientRole() ); copier.copyReferences(); Patient patient = ( Patient ) copier.copy( recordTarget.getPatientRole().getPatient() ); copier.copyReferences();
See ValidationClass EcoreUtil.Copier (EMF Javadoc) for more information.
Here's a full (JUnit) example of deep-copying. The difference is only what the original document happened to use in element, <ClinicalDocument ... />, which is pretty arbitrary:
original: <ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:hl7-org:v3" xmlns:sdtc="urn:hl7-org:sdtc" xmlns:voc="urn:hl7-org:v3/voc"> copy: <ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:hl7-org:v3" xsi:schemaLocation="urn:hl7-org:v3 infrastructure/cda/CDA_SDTC.xsd">
What's in the copy was put there by MDHT. The point is that the MDHT deep-copying framework is reliable and simple to use. Here's the code:
package com.windofkelta.mdht; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import static org.junit.Assert.assertEquals; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.EObject; import org.eclipse.mdht.uml.cda.ClinicalDocument; import org.eclipse.mdht.uml.cda.util.CDAUtil; import com.windofkelta.utilities.StreamUtilities; public class EcoreUtilCopierTests { @Test public void testCopyingMdhtResources() throws Exception { final String CCD_CONTENT = TestUtilities.getLinesInFile( TestUtilities.TEST_FODDER + "raw-ccd-tiny.xml" ); ClinicalDocument original = CDAUtil.load( new ByteArrayInputStream( CCD_CONTENT.getBytes() ) ); EcoreUtil.Copier copier = new EcoreUtil.Copier(); EObject __copy = copier.copy( original ); copier.copyReferences(); ClinicalDocument copy = ( ClinicalDocument ) __copy; String originalString = clinicalDocumentToString( original ); String copyString = clinicalDocumentToString( copy ); /* Here, the only thing different is at the tail-end of the first element: * original: <ClinicalDocument ... xmlns:sdtc="urn:hl7-org:sdtc" xmlns:voc="urn:hl7-org:v3/voc"> * copy: <ClinicalDocument ... xsi:schemaLocation="urn:hl7-org:v3 infrastructure/cda/CDA_SDTC.xsd"> */ assertEquals( originalString, copyString ); } /** Simple method to dump the clinical document to a string. This is a good, general "how-to." */ private static String clinicalDocumentToString( ClinicalDocument document ) throws Exception { OutputStream outputStream = new ByteArrayOutputStream(); CDAUtil.save( document, outputStream ); return StreamUtilities.convertOutputStreamToString( outputStream ); } }
Note that validation requires a call to CDAUtil.loadPackages(), but ordinary use of MDHT library APIs (to generate a CCD) doesn't involve it.
<properties> <!-- MDHT, Eclipse and other MDHT-relevant versions: --> <eclipse-emf.version>2.12.0.v20160420-0247</eclipse-emf.version> <eclipse-mdht.version>3.0.0.201802220601</eclipse-mdht.version> <openhealthtools.version>3.0.3.20180222</openhealthtools.version> </properties> <repositories> <repository> <id>maven-releases</id> <name>internal-libraries</name> <url>file://${project.basedir}/lib</url> </repository> </repositories> <dependency> <groupId>lpg.runtime.java</groupId> <artifactId>lpg.runtime.java</artifactId> <version>2.0.17.v201004271640</version> </dependency> <dependency> <groupId>org.eclipse.emf.ecore.xmi</groupId> <artifactId>org.eclipse.emf.ecore.xmi</artifactId> <version>${eclipse-emf.version}</version> </dependency> <dependency> <groupId>org.eclipse.emf.ecore</groupId> <artifactId>org.eclipse.emf.ecore</artifactId> <version>${eclipse-emf.version}</version> </dependency> <dependency> <groupId>org.eclipse.emf.common</groupId> <artifactId>org.eclipse.emf.common</artifactId> <version>${eclipse-emf.version}</version> </dependency> <dependency> <groupId>org.eclipse.ocl</groupId> <artifactId>org.eclipse.ocl</artifactId> <version>3.6.0.v20160523-1914</version> </dependency> <dependency> <groupId>org.eclipse.ocl.common</groupId> <artifactId>org.eclipse.ocl.common</artifactId> <version>1.4.0.v20160521-2033</version> </dependency> <dependency> <groupId>org.eclipse.ocl.ecore</groupId> <artifactId>org.eclipse.ocl.ecore</artifactId> <version>3.6.0.v20160523-1914</version> </dependency> <dependency> <groupId>org.eclipse.uml2.common</groupId> <artifactId>org.eclipse.uml2.common</artifactId> <version>2.1.0.v20170227-0935</version> </dependency> <dependency> <groupId>org.eclipse.uml2.types</groupId> <artifactId>org.eclipse.uml2.types</artifactId> <version>2.0.0.v20170227-0935</version> </dependency> <!-- MDHT Core dependencies --> <dependency> <groupId>org.eclipse.mdht.emf.runtime</groupId> <artifactId>org.eclipse.mdht.emf.runtime</artifactId> <version>${eclipse-mdht.version}</version> </dependency> <!-- MDHT CDA dependencies --> <dependency> <groupId>org.eclipse.mdht.uml.hl7.vocab</groupId> <artifactId>org.eclipse.mdht.uml.hl7.vocab</artifactId> <version>${eclipse-mdht.version}</version> </dependency> <dependency> <groupId>org.eclipse.mdht.uml.hl7.datatypes</groupId> <artifactId>org.eclipse.mdht.uml.hl7.datatypes</artifactId> <version>${eclipse-mdht.version}</version> </dependency> <dependency> <groupId>org.eclipse.mdht.uml.hl7.rim</groupId> <artifactId>org.eclipse.mdht.uml.hl7.rim</artifactId> <version>${eclipse-mdht.version}</version> </dependency> <dependency> <groupId>org.eclipse.mdht.uml.cda</groupId> <artifactId>org.eclipse.mdht.uml.cda</artifactId> <version>${eclipse-mdht.version}</version> </dependency> <!-- MDHT CDA Implementation Guide dependencies --> <dependency> <groupId>org.openhealthtools.mdht.uml.cda.consol2</groupId> <artifactId>org.openhealthtools.mdht.uml.cda.consol2</artifactId> <version>${openhealthtools.version}</version> </dependency> <!-- HL7 CDA dependencies --> <dependency> <groupId>org.hl7.cbcc.privacy.consentdirective</groupId> <artifactId>org.hl7.cbcc.privacy.consentdirective</artifactId> <version>1.0.0.20170920</version> </dependency> <dependency> <groupId>org.hl7.security.ds4p.contentprofile</groupId> <artifactId>org.hl7.security.ds4p.contentprofile</artifactId> <version>3.0.0.20170920</version> </dependency>
(For help dealing with using static copies of JARs out of a local subdirectory as is shown here, please read this note and others following for ways to do that. Please note that the .jar files are obtained from the web, Sean Muir, etc. while all other files, including .jar.md5, etc. are generated by the commands noted at this URL.) Below is an illustration of these static copies of JARs pieced together in such a way as to permit their direct use from Maven pom.xml.
The JAR list. There is no way to reach current versions of these via Maven. You can only include them statically in your (IntelliJ IDEA or Eclipse) project.
russ@nargothrond ~/sandboxes/mdht-sample $ tree lib lib ├── lpg │ └──runtime │ └──java │ └── lpg.runtime.java │ ├── 2.0.17.v201004271640 │ │ ├── lpg.runtime.java-2.0.17.v201004271640.jar │ │ ├── lpg.runtime.java-2.0.17.v201004271640.jar.md5 │ │ ├── lpg.runtime.java-2.0.17.v201004271640.jar.sha1 │ │ ├── lpg.runtime.java-2.0.17.v201004271640.pom │ │ ├── lpg.runtime.java-2.0.17.v201004271640.pom.md5 │ │ └──lpg.runtime.java-2.0.17.v201004271640.pom.sha1 │ ├── maven-metadata-local.xml │ ├── maven-metadata-local.xml.md5 │ └── maven-metadata-local.xml.sha1 └── org ├── eclipse │ ├── emf │ │ ├── common │ │ │ └── org.eclipse.emf.common │ │ │ ├── 2.12.0.v20160420-0247 │ │ │ │ ├── org.eclipse.emf.common-2.12.0.v20160420-0247.jar │ │ │ │ ├── org.eclipse.emf.common-2.12.0.v20160420-0247.jar.md5 │ │ │ │ ├── org.eclipse.emf.common-2.12.0.v20160420-0247.jar.sha1 │ │ │ │ ├── org.eclipse.emf.common-2.12.0.v20160420-0247.pom │ │ │ │ ├── org.eclipse.emf.common-2.12.0.v20160420-0247.pom.md5 │ │ │ │ └── org.eclipse.emf.common-2.12.0.v20160420-0247.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── ecore │ │ ├── org.eclipse.emf.ecore │ │ │ ├── 2.12.0.v20160420-0247 │ │ │ │ ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.jar │ │ │ │ ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.jar.md5 │ │ │ │ ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.jar.sha1 │ │ │ │ ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.pom │ │ │ │ ├── org.eclipse.emf.ecore-2.12.0.v20160420-0247.pom.md5 │ │ │ │ └── org.eclipse.emf.ecore-2.12.0.v20160420-0247.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── xmi │ │ └── org.eclipse.emf.ecore.xmi │ │ ├── 2.12.0.v20160420-0247 │ │ │ ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.jar │ │ │ ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.jar.md5 │ │ │ ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.jar.sha1 │ │ │ ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.pom │ │ │ ├── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.pom.md5 │ │ │ └── org.eclipse.emf.ecore.xmi-2.12.0.v20160420-0247.pom.sha1 │ │ ├── maven-metadata-local.xml │ │ ├── maven-metadata-local.xml.md5 │ │ └── maven-metadata-local.xml.sha1 │ ├── mdht │ │ ├── emf │ │ │ └── runtime │ │ │ └── org.eclipse.mdht.emf.runtime │ │ │ ├── 3.0.0.201802220601 │ │ │ │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.jar │ │ │ │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.jar.md5 │ │ │ │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.jar.sha1 │ │ │ │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.pom │ │ │ │ ├── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.pom.md5 │ │ │ │ └── org.eclipse.mdht.emf.runtime-3.0.0.201802220601.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── uml │ │ ├── cda │ │ │ └── org.eclipse.mdht.uml.cda │ │ │ ├── 3.0.0.201802220601 │ │ │ │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.jar │ │ │ │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.jar.md5 │ │ │ │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.jar.sha1 │ │ │ │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.pom │ │ │ │ ├── org.eclipse.mdht.uml.cda-3.0.0.201802220601.pom.md5 │ │ │ │ └── org.eclipse.mdht.uml.cda-3.0.0.201802220601.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── hl7 │ │ ├── datatypes │ │ │ └── org.eclipse.mdht.uml.hl7.datatypes │ │ │ ├── 3.0.0.201802220601 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.jar │ │ │ │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.jar.md5 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.jar.sha1 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.pom │ │ │ │ ├── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.pom.md5 │ │ │ │ └── org.eclipse.mdht.uml.hl7.datatypes-3.0.0.201802220601.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ ├── rim │ │ │ └── org.eclipse.mdht.uml.hl7.rim │ │ │ ├── 3.0.0.201802220601 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.jar │ │ │ │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.jar.md5 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.jar.sha1 │ │ │ │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.pom │ │ │ │ ├── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.pom.md5 │ │ │ │ └── org.eclipse.mdht.uml.hl7.rim-3.0.0.201802220601.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── vocab │ │ └── org.eclipse.mdht.uml.hl7.vocab │ │ ├── 3.0.0.201802220601 │ │ │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.jar │ │ │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.jar.md5 │ │ │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.jar.sha1 │ │ │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.pom │ │ │ ├── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.pom.md5 │ │ │ └── org.eclipse.mdht.uml.hl7.vocab-3.0.0.201802220601.pom.sha1 │ │ ├── maven-metadata-local.xml │ │ ├── maven-metadata-local.xml.md5 │ │ └── maven-metadata-local.xml.sha1 │ ├── ocl │ │ ├── common │ │ │ └── org.eclipse.ocl.common │ │ │ ├── 1.4.0.v20160521-2033 │ │ │ │ ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.jar │ │ │ │ ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.jar.md5 │ │ │ │ ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.jar.sha1 │ │ │ │ ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.pom │ │ │ │ ├── org.eclipse.ocl.common-1.4.0.v20160521-2033.pom.md5 │ │ │ │ └── org.eclipse.ocl.common-1.4.0.v20160521-2033.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ ├── ecore │ │ │ └── org.eclipse.ocl.ecore │ │ │ ├── 3.6.0.v20160523-1914 │ │ │ │ ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.jar │ │ │ │ ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.jar.md5 │ │ │ │ ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.jar.sha1 │ │ │ │ ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.pom │ │ │ │ ├── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.pom.md5 │ │ │ │ └── org.eclipse.ocl.ecore-3.6.0.v20160523-1914.pom.sha1 │ │ │ ├── maven-metadata-local.xml │ │ │ ├── maven-metadata-local.xml.md5 │ │ │ └── maven-metadata-local.xml.sha1 │ │ └── org.eclipse.ocl │ │ ├── 3.6.0.v20160523-1914 │ │ │ ├── org.eclipse.ocl-3.6.0.v20160523-1914.jar │ │ │ ├── org.eclipse.ocl-3.6.0.v20160523-1914.jar.md5 │ │ │ ├── org.eclipse.ocl-3.6.0.v20160523-1914.jar.sha1 │ │ │ ├── org.eclipse.ocl-3.6.0.v20160523-1914.pom │ │ │ ├── org.eclipse.ocl-3.6.0.v20160523-1914.pom.md5 │ │ │ └── org.eclipse.ocl-3.6.0.v20160523-1914.pom.sha1 │ │ ├── maven-metadata-local.xml │ │ ├── maven-metadata-local.xml.md5 │ │ └── maven-metadata-local.xml.sha1 │ └── uml2 │ ├── common │ │ └── org.eclipse.uml2.common │ │ ├── 2.1.0.v20170227-0935 │ │ │ ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.jar │ │ │ ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.jar.md5 │ │ │ ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.jar.sha1 │ │ │ ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.pom │ │ │ ├── org.eclipse.uml2.common-2.1.0.v20170227-0935.pom.md5 │ │ │ └── org.eclipse.uml2.common-2.1.0.v20170227-0935.pom.sha1 │ │ ├── maven-metadata-local.xml │ │ ├── maven-metadata-local.xml.md5 │ │ └── maven-metadata-local.xml.sha1 │ └── types │ └── org.eclipse.uml2.types │ ├── 2.0.0.v20170227-0935 │ │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar │ │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar.md5 │ │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.jar.sha1 │ │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom │ │ ├── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom.md5 │ │ └── org.eclipse.uml2.types-2.0.0.v20170227-0935.pom.sha1 │ ├── maven-metadata-local.xml │ ├── maven-metadata-local.xml.md5 │ └── maven-metadata-local.xml.sha1 ├── hl7 │ ├── cbcc │ │ └── privacy │ │ └── consentdirective │ │ └── org.hl7.cbcc.privacy.consentdirective │ │ ├── 1.0.0.20170920 │ │ │ ├── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.jar │ │ │ ├── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.jar.md5 │ │ │ ├── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.jar.sha1 │ │ │ ├── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.pom │ │ │ ├── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.pom.md5 │ │ │ └── org.hl7.cbcc.privacy.consentdirective-1.0.0.20170920.pom.sha1 │ │ ├── maven-metadata-local.xml │ │ ├── maven-metadata-local.xml.md5 │ │ └── maven-metadata-local.xml.sha1 │ └── security │ └── ds4p │ └── contentprofile │ └── org.hl7.security.ds4p.contentprofile │ ├── 3.0.0.20170920 │ │ ├── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.jar │ │ ├── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.jar.md5 │ │ ├── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.jar.sha1 │ │ ├── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.pom │ │ ├── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.pom.md5 │ │ └── org.hl7.security.ds4p.contentprofile-3.0.0.20170920.pom.sha1 │ ├── maven-metadata-local.xml │ ├── maven-metadata-local.xml.md5 │ └── maven-metadata-local.xml.sha1 └── openhealthtools └── mdht └── uml └── cda ├── consol2 │ └── org.openhealthtools.mdht.uml.cda.consol2 │ ├── 3.0.3.20180222 │ │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.jar │ │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.jar.md5 │ │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.jar.sha1 │ │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.pom │ │ ├── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.pom.md5 │ │ └── org.openhealthtools.mdht.uml.cda.consol2-3.0.3.20180222.pom.sha1 │ ├── maven-metadata-local.xml │ ├── maven-metadata-local.xml.md5 │ └── maven-metadata-local.xml.sha1 └── mu2consol └── org.openhealthtools.mdht.uml.cda.mu2consol ├── 3.0.3.20180222 │ ├── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.jar │ ├── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.jar.md5 │ ├── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.jar.sha1 │ ├── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.pom │ ├── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.pom.md5 │ └── org.openhealthtools.mdht.uml.cda.mu2consol-3.0.3.20180222.pom.sha1 ├── maven-metadata-local.xml ├── maven-metadata-local.xml.md5 └── maven-metadata-local.xml.sha1 73 directories, 162 files
Typically, to create a new resource, the code is written thus (several examples here):
import org.eclipse.mdht.uml.cda.ClinicalDocument; import org.eclipse.mdht.uml.cda.ProblemSection; import org.openhealthtools.mdht.uml.cda.consol.ConsolFactory; import org.openhealthtools.mdht.uml.cda.consol.ProblemSection; ClinicalDocument document = ConsolFactory.eINSTANCE.createContinuityOfCareDocument2(); ProblemSection section = ConsolFactory.eINSTANCE.createProblemSection();
...or to create or add a new piece of data:
import org.eclipse.mdht.uml.hl7.datatypes.CD; import org.eclipse.mdht.uml.hl7.datatypes.DatatypesFactory; CD cd = DatatypesFactory.eINSTANCE.createCD();
However, if you're performing more "eclectic" surgery, you might notice that there's no easy way to create a new RecordTarget for hosting a Patient you have in hand. However, it's possible to add that to the document since there is a method to do so. You'll see in the debugger that this also has the effect of adding new RecordTarget:
import org.eclipse.mdht.uml.cda.ClinicalDocument; public void foo( Patient patient ) { ClinicalDocument document = ConsolFactory.eINSTANCE.createContinuityOfCareDocument2(); document.addPatient( patient ); }
...of any construct whose effect you wish to see or experiment with. In this example, we're going to test the effect of setting an observation value in different ways.
( You may ask, "What's the point of this example?" The answer is that, sometimes, you have to experiment to make sure you know what you're going to get. In this case, there's a real problem with observation values. Practically speaking, it's a lot of work to generate decimal-less output in the CCD if you want that. [See highlighted output line after code sample.] You'll have to pick BigDecimal to do it. And you're advised by Sonar Rules not to do that. So what do you do? If it's a round number, use BigDecimal and use the safer Double if there's going to be a decimal? Or just use a floating-point value no matter what? )
The most useful aspect to this example is, in general, the use of
CDAUtil.saveSnippet( EObject, OutputStream )
to yield a snapshot of what you're going to see in the generated CCD (or QRDA I or Care Plan, etc.).
package com.windofkeltia.mdht; import java.math.BigDecimal; import org.eclipse.mdht.uml.cda.CDAFactory; import org.eclipse.mdht.uml.cda.Observation; import org.eclipse.mdht.uml.cda.util.CDAUtil; import org.eclipse.mdht.uml.hl7.datatypes.PQ; public class MdhtValueTests { @Test public void test() throws Exception { Observation observation = CDAFactory.eINSTANCE.createObservation(); PQ pq = DatatypesFactory.eINSTANCE.createPQ(); pq.setValue( Double.valueOf( 1 ) ); observation.getValues().add( pq ); PQ pq2 = DatatypesFactory.eINSTANCE.createPQ(); pq2.setValue( Double.valueOf( 1.1 ) ); observation.getValues().add( pq2 ); pq2.setValue( BigDecimal.valueOf( 2 ) ); observation.getValues().add( pq3 ); PQ pq4 = DatatypesFactory.eINSTANCE.createPQ(); pq2.setValue( BigDecimal.valueOf( 2.1 ) ); observation.getValues().add( pq4 ); CDAUtil.saveSnippet( observation, System.out ); } }
Note that the only acceptable input types for PQ.setTypeValue() are Double and BigDecimal:
Produces:
<observation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:hl7-org:v3"> <value xsi:type="PQ" value="1.0"/> <value xsi:type="PQ" value="1.1"/> <value xsi:type="PQ" value="2"/> <value xsi:type="PQ" value="2.1"/> </observation>
On the disquieting use of BigDecimal, see note: Sonar Rules: Java static code analysis.