001package org.hl7.fhir.convertors.misc.argonaut;
002
003import java.io.ByteArrayOutputStream;
004import java.io.File;
005import java.io.FileInputStream;
006import java.io.IOException;
007import java.util.ArrayList;
008import java.util.Collections;
009import java.util.HashMap;
010import java.util.HashSet;
011import java.util.List;
012import java.util.Map;
013import java.util.Set;
014
015/*
016  Copyright (c) 2011+, HL7, Inc.
017  All rights reserved.
018  
019  Redistribution and use in source and binary forms, with or without modification, 
020  are permitted provided that the following conditions are met:
021    
022   * Redistributions of source code must retain the above copyright notice, this 
023     list of conditions and the following disclaimer.
024   * Redistributions in binary form must reproduce the above copyright notice, 
025     this list of conditions and the following disclaimer in the documentation 
026     and/or other materials provided with the distribution.
027   * Neither the name of HL7 nor the names of its contributors may be used to 
028     endorse or promote products derived from this software without specific 
029     prior written permission.
030  
031  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
032  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
033  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
034  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
035  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
036  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
037  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
038  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
039  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
040  POSSIBILITY OF SUCH DAMAGE.
041  
042 */
043
044
045import org.apache.commons.io.IOUtils;
046import org.fhir.ucum.UcumEssenceService;
047import org.fhir.ucum.UcumService;
048import org.hl7.fhir.convertors.misc.CDAUtilities;
049import org.hl7.fhir.convertors.misc.Convert;
050import org.hl7.fhir.convertors.misc.ConverterBase;
051import org.hl7.fhir.convertors.misc.ccda.CcdaExtensions;
052import org.hl7.fhir.dstu3.context.SimpleWorkerContext;
053import org.hl7.fhir.dstu3.formats.IParser.OutputStyle;
054import org.hl7.fhir.dstu3.formats.JsonParser;
055import org.hl7.fhir.dstu3.formats.XmlParser;
056import org.hl7.fhir.dstu3.model.Address;
057import org.hl7.fhir.dstu3.model.AllergyIntolerance;
058import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceReactionComponent;
059import org.hl7.fhir.dstu3.model.Base;
060import org.hl7.fhir.dstu3.model.Binary;
061import org.hl7.fhir.dstu3.model.BooleanType;
062import org.hl7.fhir.dstu3.model.CodeableConcept;
063import org.hl7.fhir.dstu3.model.Coding;
064import org.hl7.fhir.dstu3.model.Condition;
065import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus;
066import org.hl7.fhir.dstu3.model.ContactPoint;
067import org.hl7.fhir.dstu3.model.DocumentReference;
068import org.hl7.fhir.dstu3.model.DocumentReference.DocumentReferenceContentComponent;
069import org.hl7.fhir.dstu3.model.DocumentReference.DocumentReferenceContextComponent;
070import org.hl7.fhir.dstu3.model.DomainResource;
071import org.hl7.fhir.dstu3.model.Dosage;
072import org.hl7.fhir.dstu3.model.Encounter;
073import org.hl7.fhir.dstu3.model.Encounter.EncounterHospitalizationComponent;
074import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
075import org.hl7.fhir.dstu3.model.Enumerations.DocumentReferenceStatus;
076import org.hl7.fhir.dstu3.model.Extension;
077import org.hl7.fhir.dstu3.model.Factory;
078import org.hl7.fhir.dstu3.model.HumanName;
079import org.hl7.fhir.dstu3.model.Identifier;
080import org.hl7.fhir.dstu3.model.Immunization;
081import org.hl7.fhir.dstu3.model.Immunization.ImmunizationExplanationComponent;
082import org.hl7.fhir.dstu3.model.Immunization.ImmunizationStatus;
083import org.hl7.fhir.dstu3.model.InstantType;
084import org.hl7.fhir.dstu3.model.ListResource;
085import org.hl7.fhir.dstu3.model.ListResource.ListMode;
086import org.hl7.fhir.dstu3.model.ListResource.ListStatus;
087import org.hl7.fhir.dstu3.model.Location;
088import org.hl7.fhir.dstu3.model.Medication;
089import org.hl7.fhir.dstu3.model.MedicationStatement;
090import org.hl7.fhir.dstu3.model.MedicationStatement.MedicationStatementStatus;
091import org.hl7.fhir.dstu3.model.Narrative;
092import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
093import org.hl7.fhir.dstu3.model.Observation;
094import org.hl7.fhir.dstu3.model.Observation.ObservationRelationshipType;
095import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
096import org.hl7.fhir.dstu3.model.Organization;
097import org.hl7.fhir.dstu3.model.Patient;
098import org.hl7.fhir.dstu3.model.Period;
099import org.hl7.fhir.dstu3.model.Practitioner;
100import org.hl7.fhir.dstu3.model.Procedure;
101import org.hl7.fhir.dstu3.model.Procedure.ProcedurePerformerComponent;
102import org.hl7.fhir.dstu3.model.Procedure.ProcedureStatus;
103import org.hl7.fhir.dstu3.model.Reference;
104import org.hl7.fhir.dstu3.model.Resource;
105import org.hl7.fhir.dstu3.model.StringType;
106import org.hl7.fhir.dstu3.model.StructureDefinition;
107import org.hl7.fhir.dstu3.model.Timing;
108import org.hl7.fhir.dstu3.model.Type;
109import org.hl7.fhir.dstu3.utils.NarrativeGenerator;
110import org.hl7.fhir.dstu3.utils.ResourceUtilities;
111import org.hl7.fhir.utilities.FileUtilities;
112import org.hl7.fhir.utilities.Utilities;
113import org.hl7.fhir.utilities.ZipGenerator;
114import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
115import org.hl7.fhir.utilities.validation.ValidationMessage;
116import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
117import org.hl7.fhir.utilities.xhtml.NodeType;
118import org.hl7.fhir.utilities.xhtml.XhtmlNode;
119import org.hl7.fhir.utilities.xml.XMLUtil;
120import org.w3c.dom.Element;
121
122public class ArgonautConverter extends ConverterBase {
123  //  public final static String DEF_TS_SERVER = "http://fhir-dev.healthintersections.com.au/open";
124  public final static String DEV_TS_SERVER = "http://local.fhir.org:8080/open";
125  public static final String UCUM_PATH = "c:\\work\\org.hl7.fhir\\build\\implementations\\java\\org.hl7.fhir.convertors\\samples\\ucum-essence.xml";
126  public static final String SRC_PATH = "c:\\work\\org.hl7.fhir\\build\\publish\\";
127  private static final String DEFAULT_ID_SPACE = "urn:uuid:e8e06b15-0f74-4b8e-b5e2-609dae7119dc";
128
129  private static final boolean WANT_SAVE = true;
130  private static final boolean WANT_VALIDATE = false;
131  private final UcumService ucumSvc;
132  //    private ValidationEngine validator;
133  private final SimpleWorkerContext context;
134  private final Map<String, Map<String, Integer>> sections = new HashMap<>();
135  private final Map<String, Practitioner> practitionerCache = new HashMap<>();
136  private final Set<String> oids = new HashSet<>();
137  private final Map<String, ZipGenerator> zipsX = new HashMap<>();
138  private final Map<String, ZipGenerator> zipsJ = new HashMap<>();
139  private final Map<String, Stats> stats = new HashMap<>();
140  public int perfCount;
141  int errors = 0;
142  int warnings = 0;
143  Map<String, Integer> procCodes = new HashMap<>();
144  Map<String, Integer> condCodes = new HashMap<>();
145  Map<String, Integer> allergyCodes = new HashMap<>();
146  private String destFolder;
147  private ZipGenerator zipJ;
148  private ZipGenerator zipX;
149
150  public ArgonautConverter(UcumService ucumSvc, String path) throws Exception {
151    super();
152    this.ucumSvc = ucumSvc;
153    context = SimpleWorkerContext.fromPack(path);
154//              validator = new ValidationEngine();
155//              validator.readDefinitions(path);
156//              validator.setAnyExtensionsAllowed(true);
157  }
158
159  public static void main(String[] args) {
160    try {
161      ArgonautConverter c = new ArgonautConverter(new UcumEssenceService(UCUM_PATH), Utilities.path(SRC_PATH, "validation.xml.zip"));
162      c.destFolder = "C:\\work\\com.healthintersections.fhir\\argonaut\\fhir";
163      c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\file_emergency", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("EMER"));
164      c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\file_ed", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("IMP"));
165      c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\fileX", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB"));
166      c.printSectionSummaries();
167      c.closeZips();
168      System.out.println("All done. " + c.getErrors() + " errors, " + c.getWarnings() + " warnings");
169    } catch (Exception e) {
170      e.printStackTrace();
171    }
172  }
173
174  public int getErrors() {
175    return errors;
176  }
177
178  public int getWarnings() {
179    return warnings;
180  }
181
182  public void convert(String sourceFolder, Coding clss) throws Exception {
183    File source = ManagedFileAccess.file(sourceFolder);
184    for (String f : source.list()) {
185      convert(sourceFolder, f, clss);
186    }
187  }
188
189  private void closeZips() throws Exception {
190    for (ZipGenerator z : zipsJ.values())
191      z.close();
192    for (ZipGenerator z : zipsX.values())
193      z.close();
194  }
195
196  public void printSectionSummaries() {
197    System.out.println("Statistics:");
198    for (String n : sorted(stats.keySet())) {
199      Stats s = stats.get(n);
200      System.out.println("  " + n + ": generated " + s.getInstances() + ", errors " + s.getErrors() + ", warnings " + s.getWarnings());
201    }
202
203    System.out.println("OIDs:");
204    for (String n : sorted(oids))
205      System.out.println("  " + n);
206
207    for (String n : sections.keySet()) {
208      System.out.println(n + " Analysis");
209      Map<String, Integer> s = sections.get(n);
210      for (String p : sorted(s.keySet())) {
211        System.out.println("  " + p + ": " + s.get(p));
212      }
213    }
214
215    dumpCodes();
216  }
217
218  private List<String> sorted(Set<String> keys) {
219    List<String> names = new ArrayList<>();
220    names.addAll(keys);
221    Collections.sort(names);
222    return names;
223  }
224
225  private void convert(String sourceFolder, String filename, Coding clss) throws IOException {
226    if (ManagedFileAccess.file(Utilities.path(sourceFolder, filename)).length() == 0)
227      return;
228
229    CDAUtilities cda;
230    try {
231      System.out.println("Process " + Utilities.path(sourceFolder, filename));
232      cda = new CDAUtilities(ManagedFileAccess.inStream(Utilities.path(sourceFolder, filename)));
233      zipJ = new ZipGenerator(Utilities.path(destFolder, "json/doc", FileUtilities.changeFileExt(filename, ".json.zip")));
234      zipX = new ZipGenerator(Utilities.path(destFolder, "xml/doc", FileUtilities.changeFileExt(filename, ".xml.zip")));
235      Element doc = cda.getElement();
236      Convert convert = new Convert(cda, ucumSvc, "-0400");
237      convert.setGenerateMissingExtensions(true);
238      Context context = new Context();
239      context.setBaseId(FileUtilities.changeFileExt(filename, ""));
240      context.setEncClass(clss);
241      makeSubject(cda, convert, doc, context, context.getBaseId() + "-patient");
242      makeAuthor(cda, convert, doc, context, context.getBaseId() + "-author");
243      makeEncounter(cda, convert, doc, context, context.getBaseId() + "-encounter");
244      Element body = cda.getDescendent(doc, "component/structuredBody");
245      for (Element c : cda.getChildren(body, "component")) {
246        processSection(cda, convert, context, cda.getChild(c, "section"));
247      }
248      oids.addAll(convert.getOids());
249      saveResource(context.getEncounter());
250      makeBinary(sourceFolder, filename, context);
251      makeDocumentReference(cda, convert, doc, context);
252      zipJ.close();
253      zipX.close();
254    } catch (Exception e) {
255      throw new Error("Unable to process " + Utilities.path(sourceFolder, filename) + ": " + e.getMessage(), e);
256    }
257  }
258
259  private void processSection(CDAUtilities cda, Convert convert, Context context, Element section) throws Exception {
260    checkNoSubject(cda, section, "Section");
261
262    // this we do by templateId
263    if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.11") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.5.1"))
264      processProblemsSection(cda, convert, section, context);
265    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.12") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.7.1"))
266      processProcedureSection(cda, convert, section, context);
267    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.3") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.22.1"))
268      processEncountersSection(cda, convert, section, context);
269    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.6.1"))
270      processAllergiesSection(cda, convert, section, context);
271    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.2.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.6"))
272      processImmunizationsSection(cda, convert, section, context);
273    else if (cda.hasTemplateId(section, "1.3.6.1.4.1.19376.1.5.3.1.3.1"))
274      processReasonForEncounter(cda, convert, section, context);
275    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.3.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.14"))
276      processResultsSection(cda, convert, section, context);
277    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.4.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.16"))
278      processVitalSignsSection(cda, convert, section, context);
279    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.1.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.8"))
280      processMedicationsSection(cda, convert, section, context);
281    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.17") || cda.hasTemplateId(section, "2.16.840.1.113883.3.88.11.83.126"))
282      processSocialHistorySection(cda, convert, section, context);
283    else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.9"))
284      scanSection("Payers", section);
285    else
286      throw new Exception("Unprocessed section " + cda.getChild(section, "title").getTextContent());
287  }
288
289  private void checkNoSubject(CDAUtilities cda, Element act, String path) throws Exception {
290    if (cda.getChild(act, "subject") != null)
291      throw new Exception("The conversion program cannot accept a subject at the location " + path);
292  }
293
294  private void scanSection(String name, Element child) {
295    Map<String, Integer> section;
296    if (sections.containsKey(name))
297      section = sections.get(name);
298    else {
299      section = new HashMap<>();
300      sections.put(name, section);
301    }
302    iterateChildren(section, "/", child);
303  }
304
305  private void iterateChildren(Map<String, Integer> section, String path, Element element) {
306    Element child = XMLUtil.getFirstChild(element);
307    while (child != null) {
308      String pathC = path + child.getNodeName() + attributes(child);
309      if (section.containsKey(pathC))
310        section.put(pathC, section.get(pathC) + 1);
311      else
312        section.put(pathC, 1);
313      iterateChildren(section, pathC + "/", child);
314      child = XMLUtil.getNextSibling(child);
315    }
316  }
317
318  private String attributes(Element child) {
319    String s = ",";
320    if (child.hasAttribute("inversionInd"))
321      s += "inversionInd:" + child.getAttribute("inversionInd") + ",";
322    if (child.hasAttribute("negationInd"))
323      s += "negationInd:" + child.getAttribute("negationInd") + ",";
324    if (child.hasAttribute("nullFlavor"))
325      s += "nullFlavor:" + child.getAttribute("nullFlavor") + ",";
326    if (child.hasAttribute("xsi:type"))
327      s += "type:" + child.getAttribute("xsi:type") + ",";
328    s = s.substring(0, s.length() - 1);
329
330    if (child.getNodeName().equals("statusCode"))
331      return "[code:" + child.getAttribute("code") + "]";
332    if (child.getNodeName().equals("temnplateId"))
333      return "[id:" + child.getAttribute("root") + "]";
334    else if (child.hasAttribute("moodCode"))
335      return "[" + child.getAttribute("classCode") + "," + child.getAttribute("moodCode") + s + "]";
336    else if (child.hasAttribute("classCode"))
337      return "[" + child.getAttribute("classCode") + s + "]";
338    else if (child.hasAttribute("typeCode"))
339      return "[" + child.getAttribute("typeCode") + s + "]";
340    else if (Utilities.noString(s))
341      return "";
342    else
343      return "[" + s.substring(1) + "]";
344  }
345
346  private void saveResource(Resource resource) throws Exception {
347    saveResource(resource, null);
348  }
349
350  private void saveResource(Resource resource, String extraType) throws Exception {
351    if (!WANT_SAVE)
352      return;
353
354    DomainResource dr = null;
355    if (resource instanceof DomainResource) {
356      dr = (DomainResource) resource;
357      if (!dr.hasText()) {
358        NarrativeGenerator generator = new NarrativeGenerator("", "", context);
359        generator.generate(dr);
360      }
361    }
362    XmlParser xparser = new XmlParser();
363    xparser.setOutputStyle(OutputStyle.PRETTY);
364    JsonParser jparser = new JsonParser();
365    jparser.setOutputStyle(OutputStyle.PRETTY);
366
367    ByteArrayOutputStream ba = new ByteArrayOutputStream();
368    xparser.compose(ba, resource);
369    ba.close();
370    byte[] srcX = ba.toByteArray();
371    ba = new ByteArrayOutputStream();
372    jparser.compose(ba, resource);
373    ba.close();
374    byte[] srcJ = ba.toByteArray();
375
376    String rn = resource.getResourceType().toString();
377    if (extraType != null)
378      rn = rn + extraType;
379    zipX.addBytes(resource.getId() + ".xml", srcX, false);
380    zipJ.addBytes(resource.getId() + ".json", srcJ, false);
381    if (!zipsX.containsKey(rn)) {
382      zipsX.put(rn, new ZipGenerator(Utilities.path(destFolder, "xml/type", rn + ".xml.zip")));
383      zipsJ.put(rn, new ZipGenerator(Utilities.path(destFolder, "json/type", rn + ".json.zip")));
384      stats.put(rn, new Stats());
385    }
386
387    zipsJ.get(rn).addBytes(resource.getId() + ".json", srcJ, false);
388    zipsX.get(rn).addBytes(resource.getId() + ".xml", srcX, false);
389    Stats ss = stats.get(rn);
390    ss.setInstances(ss.getInstances() + 1);
391
392    String profile = resource.getUserString("profile");
393    validate(srcX, profile, resource, ss);
394  }
395
396  private void validate(byte[] src, String url, Resource resource, Stats stats) throws Exception {
397    if (!WANT_VALIDATE)
398      return;
399    if (url == null)
400      url = "http://hl7.org/fhir/StructureDefinition/" + resource.getResourceType().toString();
401    StructureDefinition def = context.fetchResource(StructureDefinition.class, url);
402    if (def == null)
403      throw new Exception("Unable to find Structure Definition " + url);
404
405//              validator.reset();
406//              validator.setProfile(def);
407//              validator.setSource(src);
408//              validator.process();
409    List<ValidationMessage> msgs = null; // validator.getOutputs();
410    boolean ok = false;
411    boolean first = true;
412    for (ValidationMessage m : msgs) {
413      if (m.getLevel() == IssueSeverity.ERROR && !msgOk(m.getMessage())) {
414        if (first) {
415          System.out.println("  validate " + resource.getId() + ".xml against " + url);
416          first = false;
417        }
418        System.out.println("    " + m.getLevel().toCode() + ": " + m.getMessage() + " @ " + m.getLocation());
419        if (m.getLevel() == IssueSeverity.WARNING) {
420          stats.setWarnings(stats.getWarnings() + 1);
421          warnings++;
422        }
423        if (m.getLevel() == IssueSeverity.ERROR || m.getLevel() == IssueSeverity.FATAL) {
424          stats.setErrors(stats.getErrors() + 1);
425          errors++;
426        }
427      }
428
429      ok = ok && !(m.getLevel() == IssueSeverity.ERROR || m.getLevel() == IssueSeverity.FATAL);
430    }
431  }
432
433  private boolean msgOk(String message) {
434    return message.equals("Invalid Resource target type. Found Observation, but expected one of (DiagnosticReport)");
435  }
436
437  private void checkGenerateIdentifier(List<Identifier> ids, DomainResource resource) {
438    if (ids.isEmpty())
439      ids.add(new Identifier().setSystem(DEFAULT_ID_SPACE).setValue(resource.getClass().getName().toLowerCase() + "-" + resource.getId()));
440  }
441
442  private void makeSubject(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception {
443    Element rt = cda.getChild(doc, "recordTarget");
444    scanSection("Patient", rt);
445    Element pr = cda.getChild(rt, "patientRole");
446    Element p = cda.getChild(pr, "patient");
447
448    Patient pat = new Patient();
449    pat.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/patient-daf-dafpatient");
450    StringBuilder b = new StringBuilder();
451
452    pat.setId(id);
453    for (Element e : cda.getChildren(p, "name")) {
454      HumanName name = convert.makeNameFromEN(e);
455      pat.getName().add(name);
456      b.append(NarrativeGenerator.displayHumanName(name));
457      b.append(" ");
458    }
459    b.append("(");
460    for (Element e : cda.getChildren(pr, "id")) {
461      Identifier identifier = convert.makeIdentifierFromII(e);
462      pat.getIdentifier().add(identifier);
463      b.append(identifier.getValue());
464      b.append(", ");
465    }
466
467    for (Element e : cda.getChildren(pr, "addr"))
468      pat.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e)));
469    for (Element e : cda.getChildren(pr, "telecom"))
470      pat.getTelecom().add(convert.makeContactFromTEL(e));
471    pat.setGender(convert.makeGenderFromCD(cda.getChild(p, "administrativeGenderCode")));
472    b.append(pat.getGender().getDisplay());
473    b.append(", ");
474    pat.setBirthDateElement(convert.makeDateFromTS(cda.getChild(p, "birthTime")));
475    b.append("DOB: ");
476    b.append(pat.getBirthDateElement().toHumanDisplay());
477    b.append(")");
478    pat.setMaritalStatus(convert.makeCodeableConceptFromCD(cda.getChild(p, "maritalStatusCode")));
479
480    pat.addExtension(Factory.newExtension(CcdaExtensions.DAF_NAME_RACE, convert.makeCodeableConceptFromCD(cda.getChild(p, "raceCode")), false));
481    pat.addExtension(Factory.newExtension(CcdaExtensions.DAF_NAME_ETHNICITY, convert.makeCodeableConceptFromCD(cda.getChild(p, "ethnicGroupCode")), false));
482
483    pat.addExtension(Factory.newExtension(CcdaExtensions.NAME_RELIGION, convert.makeCodeableConceptFromCD(cda.getChild(p, "religiousAffiliationCode")), false));
484    pat.addExtension(Factory.newExtension(CcdaExtensions.NAME_BIRTHPLACE, convert.makeAddressFromAD(cda.getChild(p, new String[]{"birthplace", "place", "addr"})), false));
485
486    Element g = cda.getChild(p, "guardian");
487    if (g != null) {
488      Patient.ContactComponent guardian = new Patient.ContactComponent();
489      pat.getContact().add(guardian);
490      guardian.getRelationship().add(Factory.newCodeableConcept("GUARD", "urn:oid:2.16.840.1.113883.5.110", "guardian"));
491      for (Element e : cda.getChildren(g, "addr"))
492        if (guardian.getAddress() == null)
493          guardian.setAddress(makeDefaultAddress(convert.makeAddressFromAD(e)));
494      for (Element e : cda.getChildren(g, "telecom"))
495        guardian.getTelecom().add(convert.makeContactFromTEL(e));
496      g = cda.getChild(g, "guardianPerson");
497      for (Element e : cda.getChildren(g, "name"))
498        if (guardian.getName() == null)
499          guardian.setName(convert.makeNameFromEN(e));
500    }
501
502    Element l = cda.getChild(p, "languageCommunication");
503    CodeableConcept cc = new CodeableConcept();
504    Coding c = new Coding();
505    c.setSystem(ResourceUtilities.FHIR_LANGUAGE);
506    c.setCode(patchLanguage(cda.getChild(l, "languageCode").getAttribute("code")));
507    cc.getCoding().add(c);
508    pat.addCommunication().setLanguage(cc);
509
510    Element prv = cda.getChild(pr, "providerOrganization");
511    if (prv != null)
512      pat.setManagingOrganization(new Reference().setReference("Organization/" + processOrganization(prv, cda, convert, context).getId()));
513
514    context.setSubjectRef(new Reference().setDisplay(b.toString()).setReference("Patient/" + pat.getId()));
515    saveResource(pat);
516  }
517
518  private Organization processOrganization(Element oo, CDAUtilities cda, Convert convert, Context context) throws Exception {
519    Organization org = new Organization();
520    org.setId(context.getBaseId() + "-organization-" + context.getOrgId());
521    org.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/org-daf-daforganization");
522    context.setOrgId(context.getOrgId() + 1);
523    for (Element e : cda.getChildren(oo, "id"))
524      org.getIdentifier().add(convert.makeIdentifierFromII(e));
525    for (Element e : cda.getChildren(oo, "addr"))
526      org.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e)));
527    for (Element e : cda.getChildren(oo, "telecom")) {
528      ContactPoint cp = convert.makeContactFromTEL(e);
529      if (Utilities.noString(cp.getValue()))
530        cp.setValue("1 (555) 555 5555");
531      org.getTelecom().add(cp);
532    }
533    for (Element e : cda.getChildren(oo, "name"))
534      org.setName(e.getTextContent());
535    saveResource(org);
536    return org;
537  }
538
539  private Address makeDefaultAddress(Address ad) {
540    if (ad == null || ad.isEmpty()) {
541      ad = new Address();
542      ad.addLine("21 Doar road");
543      ad.setCity("Erewhon");
544      ad.setState("CA");
545      ad.setPostalCode("31233");
546    }
547    return ad;
548  }
549
550  private String patchLanguage(String lang) {
551    if (lang.equals("spa"))
552      return "es";
553    if (lang.equals("eng"))
554      return "en";
555    return lang;
556  }
557
558  ///legalAuthenticator/assignedEntity: 2979
559  ///legalAuthenticator/assignedEntity/addr: 2979
560  ///legalAuthenticator/assignedEntity/assignedPerson: 2979
561  ///legalAuthenticator/assignedEntity/id: 2979
562  ///legalAuthenticator/assignedEntity/representedOrganization: 2979
563  ///legalAuthenticator/assignedEntity/representedOrganization/addr: 2979
564  ///legalAuthenticator/assignedEntity/representedOrganization/id: 2979
565  ///legalAuthenticator/assignedEntity/representedOrganization/name: 2979
566  ///legalAuthenticator/assignedEntity/representedOrganization/telecom: 2979
567  ///legalAuthenticator/assignedEntity/telecom: 2979
568
569  private Practitioner makeAuthor(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception {
570    Element a = cda.getChild(doc, "author");
571    scanSection("Author", a);
572    Practitioner author = processPerformer(cda, convert, context, a, "assignedAuthor", "assignedPerson");
573    context.setAuthorRef(new Reference().setDisplay(author.getUserString("display")).setReference("Practitioner/" + author.getId()));
574    return author;
575  }
576
577  private Practitioner makePerformer(CDAUtilities cda, Convert convert, Context context, Element eperf, String roleName, String entityName) throws Exception {
578    Element ae = cda.getChild(eperf, roleName);
579    Element ap = cda.getChild(ae, entityName);
580
581    StringBuilder b = new StringBuilder();
582
583    Practitioner perf = new Practitioner();
584    perf.setId("performer-" + perfCount);
585    perf.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/pract-daf-dafpract");
586    perfCount++;
587    for (Element e : cda.getChildren(ae, "id")) {
588      Identifier id = convert.makeIdentifierFromII(e);
589      perf.getIdentifier().add(id);
590    }
591
592    for (Element e : cda.getChildren(ap, "name")) {
593      HumanName name = convert.makeNameFromEN(e);
594      perf.addName(name);
595      b.append(NarrativeGenerator.displayHumanName(name));
596      b.append(" ");
597    }
598    for (Element e : cda.getChildren(ae, "addr"))
599      perf.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e)));
600    boolean first = true;
601    for (Element e : cda.getChildren(ae, "telecom")) {
602      ContactPoint contact = convert.makeContactFromTEL(e);
603      perf.getTelecom().add(contact);
604      if (!Utilities.noString(contact.getValue())) {
605        if (first) {
606          b.append("(");
607          first = false;
608        } else
609          b.append(" ");
610        b.append(NarrativeGenerator.displayContactPoint(contact));
611      }
612    }
613    if (!first)
614      b.append(")");
615
616//              Element e = cda.getChild(ae, "representedOrganization");
617//              if (e != null)
618//                      perf.addRole().setOrganization(new Reference().setReference("Organization/"+processOrganization(e, cda, convert, context).getId()));
619    perf.setUserData("display", b.toString());
620    return perf;
621  }
622
623  ///serviceEvent/performer/functionCode: 9036
624  private Encounter makeEncounter(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception {
625    Element co = cda.getChild(doc, "componentOf");
626    Element ee = cda.getChild(co, "encompassingEncounter");
627    scanSection("Encounter", co);
628    Element of = cda.getChild(doc, "documentationOf");
629    Element se = cda.getChild(of, "serviceEvent");
630    scanSection("Encounter", of);
631
632    Encounter enc = new Encounter();
633    enc.setId(id);
634    enc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/encounter-daf-dafencounter");
635    context.setEncounter(enc);
636    enc.setSubject(context.getSubjectRef());
637
638    for (Element e : cda.getChildren(ee, "id"))
639      enc.getIdentifier().add(convert.makeIdentifierFromII(e));
640    checkGenerateIdentifier(enc.getIdentifier(), enc);
641
642    Period p1 = convert.makePeriodFromIVL(cda.getChild(ee, "effectiveTime"));
643    //          Period p2 = convert.makePeriodFromIVL(cda.getChild(se, "effectiveTime")); // well, what is this?
644    //          if (!Base.compareDeep(p1, p2, false))
645    //                  throw new Error("episode time mismatch: "+NarrativeGenerator.displayPeriod(p1)+" & "+NarrativeGenerator.displayPeriod(p2));
646    enc.setPeriod(p1);
647    if (p1.hasEnd())
648      enc.setStatus(EncounterStatus.FINISHED);
649    else
650      enc.setStatus(EncounterStatus.INPROGRESS);
651    enc.setClass_(context.getEncClass());
652
653    Element dd = cda.getChild(ee, "dischargeDispositionCode");
654    if (dd != null) {
655      enc.setHospitalization(new EncounterHospitalizationComponent());
656      enc.getHospitalization().setDischargeDisposition(convert.makeCodeableConceptFromCD(dd));
657    }
658    for (Element e : cda.getChildren(se, "performer")) {
659      Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson");
660      Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"));
661      if (ref != null)
662        enc.addParticipant().setIndividual(ref);
663    }
664    return enc;
665  }
666
667  private Practitioner processPerformer(CDAUtilities cda, Convert convert, Context context, Element e, String roleName, String entityName) throws Exception {
668    Practitioner perf = makePerformer(cda, convert, context, e, roleName, entityName);
669    if (perf == null)
670      return null;
671
672    Reference ref = null;
673    for (Identifier identifier : perf.getIdentifier()) {
674      String key = keyFor(identifier);
675      if (practitionerCache.containsKey(key))
676        return practitionerCache.get(key);
677    }
678
679    saveResource(perf);
680    for (Identifier identifier : perf.getIdentifier()) {
681      String key = "Practitioner-" + keyFor(identifier);
682      practitionerCache.put(key, perf);
683    }
684    return perf;
685  }
686
687  private String keyFor(Identifier identifier) {
688    return identifier.getSystem() + "||" + identifier.getValue();
689  }
690
691  private void buildNarrative(DomainResource resource, Element child) {
692    if (!Utilities.noString(child.getTextContent())) {
693      XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
694      String s = child.getTextContent().trim();
695      if (Utilities.noString(s))
696        div.addText("No Narrative provided in the source CDA document");
697      else
698        div.addText(s);
699      resource.setText(new Narrative().setStatus(NarrativeStatus.ADDITIONAL).setDiv(div));
700    }
701  }
702
703  private void processProcedureSection(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception {
704    scanSection("Procedures", sect);
705    ListResource list = new ListResource();
706    list.setId(context.getBaseId() + "-list-procedures");
707    // list.setUserData("profile", "") none?
708    list.setSubject(context.getSubjectRef());
709    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sect, "code")), null));
710    list.setTitle(cda.getChild(sect, "title").getTextContent());
711    list.setStatus(ListStatus.CURRENT);
712    list.setMode(ListMode.SNAPSHOT);
713    list.setDateElement(context.getNow());
714    list.setSource(context.getAuthorRef());
715    buildNarrative(list, cda.getChild(sect, "text"));
716
717    int i = 0;
718    for (Element c : cda.getChildren(sect, "entry")) {
719      Element p = cda.getChild(c, "procedure");
720      Procedure proc = new Procedure();
721      proc.setId(context.getBaseId() + "-procedure-" + i);
722      proc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/procedure-daf-dafprocedure");
723      i++;
724      proc.setSubject(context.getSubjectRef());
725      proc.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
726      list.addEntry().setItem(new Reference().setReference("Procedure/" + proc.getId()));
727      proc.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(p, "code")), null));
728      recordProcedureCode(proc.getCode());
729      for (Element e : cda.getChildren(p, "id"))
730        proc.getIdentifier().add(convert.makeIdentifierFromII(e));
731
732      proc.setStatus(determineProcedureStatus(cda.getChild(p, "statusCode")));
733      buildNarrative(proc, cda.getChild(p, "text"));
734      proc.setPerformed(convert.makeDateTimeFromTS(cda.getChild(p, "effectiveTime")));
735
736      for (Element e : cda.getChildren(p, "performer")) {
737        ProcedurePerformerComponent part = proc.addPerformer();
738        Practitioner pp = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson");
739        Reference ref = new Reference().setReference("Practitioner/" + pp.getId()).setDisplay(pp.getUserString("display"));
740        part.setActor(ref);
741      }
742      saveResource(proc);
743    }
744    saveResource(list);
745  }
746
747  private CodeableConcept inspectCode(CodeableConcept cc, Coding def) {
748    if (cc != null) {
749      for (Coding c : cc.getCoding()) {
750        if ("http://snomed.info/sct".equals(c.getSystem())) {
751          if ("ASSERTION".equals(c.getCode()))
752            c.setSystem("http://hl7.org/fhir/v3/ActCode");
753        }
754        if ("http://hl7.org/fhir/v3/ActCode".equals(c.getSystem()) && "ASSERTION".equals(c.getCode())) {
755          if (def == null)
756            throw new Error("need a default code");
757          c.setSystem(def.getSystem());
758          c.setVersion(def.getVersion());
759          c.setCode(def.getCode());
760          c.setDisplay(def.getDisplay());
761        }
762      }
763    }
764    return cc;
765  }
766
767  private ProcedureStatus determineProcedureStatus(Element child) {
768    if ("completed".equals(child.getAttribute("code")))
769      return ProcedureStatus.COMPLETED;
770    throw new Error("not done yet: " + child.getAttribute("code"));
771  }
772
773  private void processReasonForEncounter(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception {
774    scanSection("Reason", sect);
775    context.getEncounter().addReason().setText(cda.getChild(sect, "text").getTextContent());
776  }
777
778  private void processProblemsSection(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception {
779    scanSection("Problems", sect);
780    ListResource list = new ListResource();
781    list.setId(context.getBaseId() + "-list-problems");
782    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafproblemlist");
783    list.setSubject(context.getSubjectRef());
784    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sect, "code")), null));
785    list.setTitle(cda.getChild(sect, "title").getTextContent());
786    list.setStatus(ListStatus.CURRENT);
787    list.setMode(ListMode.SNAPSHOT);
788    list.setDateElement(context.getNow());
789    list.setSource(context.getAuthorRef());
790    buildNarrative(list, cda.getChild(sect, "text"));
791
792    int i = 0;
793    for (Element c : cda.getChildren(sect, "entry")) {
794      Element pca = cda.getChild(c, "act"); // problem concern act
795      Condition cond = new Condition();
796      cond.setId(context.getBaseId() + "-problem-" + i);
797      cond.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/condition-daf-dafcondition");
798      i++;
799      cond.setSubject(context.getSubjectRef());
800      cond.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
801      cond.setVerificationStatus(getVerificationStatusFromAct(cda.getChild(pca, "statusCode")));
802
803      cond.setAssertedDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(pca, "effectiveTime"), "low")));
804
805      boolean found = false;
806      for (Element e : cda.getChildren(pca, "id")) {
807        Identifier id = convert.makeIdentifierFromII(e);
808        cond.getIdentifier().add(id);
809      }
810      if (!found) {
811        list.addEntry().setItem(new Reference().setReference("Condition/" + cond.getId()));
812        for (Element e : cda.getChildren(pca, "performer")) {
813          if (cond.hasAsserter())
814            throw new Error("additional asserter discovered");
815          Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson");
816          Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"));
817          cond.setAsserter(ref);
818        }
819        Element po = cda.getChild(cda.getChild(pca, "entryRelationship"), "observation"); // problem observation
820        cond.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(po, "value")), null));
821        recordConditionCode(cond.getCode());
822        cond.setOnset(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(po, "effectiveTime"), "low")));
823        Element pso = cda.getChild(cda.getChild(po, "entryRelationship"), "observation"); // problem status observation
824        String status = cda.getChild(pso, "value").getAttribute("code");
825        if (status.equals("55561003"))
826          cond.setAbatement(new BooleanType("false"));
827        else
828          throw new Error("unknown status code " + status);
829        saveResource(cond);
830      }
831    }
832    saveResource(list);
833  }
834
835  private ConditionVerificationStatus getVerificationStatusFromAct(Element child) {
836    String s = child.getAttribute("code");
837    if (!"active".equals(s))
838      System.out.println(s);
839    return ConditionVerificationStatus.CONFIRMED;
840  }
841
842  private void processAllergiesSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
843    scanSection("Allergies", section);
844    ListResource list = new ListResource();
845    list.setId(context.getBaseId() + "-list-allergies");
846    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafallergylist");
847    list.setSubject(context.getSubjectRef());
848    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
849    list.setTitle(cda.getChild(section, "title").getTextContent());
850    list.setStatus(ListStatus.CURRENT);
851    list.setDateElement(context.getNow());
852    list.setSource(context.getAuthorRef());
853    list.setMode(ListMode.SNAPSHOT);
854    buildNarrative(list, cda.getChild(section, "text"));
855
856    int i = 0;
857    for (Element c : cda.getChildren(section, "entry")) {
858      Element apa = cda.getChild(c, "act"); // allergy problem act
859      AllergyIntolerance ai = new AllergyIntolerance();
860      ai.setId(context.getBaseId() + "-allergy-" + i);
861      ai.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/allergyintolerance-daf-dafallergyintolerance");
862      i++;
863      ai.setPatient(context.getSubjectRef());
864
865      ai.setAssertedDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(apa, "effectiveTime"), "low")));
866      boolean found = false;
867      for (Element e : cda.getChildren(apa, "id")) {
868        Identifier id = convert.makeIdentifierFromII(e);
869        ai.getIdentifier().add(id);
870      }
871      if (!found) {
872        list.addEntry().setItem(new Reference().setReference("AllergyIntolerance/" + ai.getId()));
873
874        Element ao = cda.getChild(cda.getChild(apa, "entryRelationship"), "observation"); // allergy observation
875        if (!cda.getChild(ao, "value").getAttribute("code").equals("419511003"))
876          throw new Error("unexpected code");
877        // nothing....
878
879        // no allergy status observation
880        List<Element> reactions = cda.getChildren(ao, "entryRelationship");
881        Element pe = cda.getChild(cda.getChild(cda.getChild(ao, "participant"), "participantRole"), "playingEntity");
882        Element pec = cda.getChild(pe, "code");
883        if (pec == null || !Utilities.noString(pec.getAttribute("nullFlavor"))) {
884          String n = cda.getChild(pe, "name").getTextContent();
885          //                            if (n.contains("No Known Drug Allergies") && reactions.isEmpty())
886          //                                    ai.setSubstance(new CodeableConcept().setText(n)); // todo: what do with this?
887          //                            else
888          ai.setCode(new CodeableConcept().setText(n));
889        } else
890          ai.setCode(inspectCode(convert.makeCodeableConceptFromCD(pec), null));
891        recordAllergyCode(ai.getCode());
892        if (!reactions.isEmpty()) {
893          AllergyIntoleranceReactionComponent aie = ai.addReaction();
894          for (Element er : reactions) {
895            Element ro = cda.getChild(er, "observation");
896            aie.addManifestation(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(ro, "value")), null));
897          }
898        }
899
900        saveResource(ai);
901      }
902    }
903    saveResource(list);
904  }
905
906  private void processVitalSignsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
907    scanSection("Vital Signs", section);
908    ListResource list = new ListResource();
909    list.setId(context.getBaseId() + "-list-vitalsigns");
910    //. list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafproblemlist"); no list
911    list.setSubject(context.getSubjectRef());
912    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
913    list.setTitle(cda.getChild(section, "title").getTextContent());
914    list.setStatus(ListStatus.CURRENT);
915    list.setMode(ListMode.SNAPSHOT);
916    list.setDateElement(context.getNow());
917    list.setSource(context.getAuthorRef());
918    buildNarrative(list, cda.getChild(section, "text"));
919
920    int i = 0;
921    for (Element c : cda.getChildren(section, "entry")) {
922      Element org = cda.getChild(c, "organizer"); // problem concern act
923      for (Element oc : cda.getChildren(org, "component")) {
924        Element o = cda.getChild(oc, "observation"); // problem concern act
925        Observation obs = new Observation();
926        obs.setId(context.getBaseId() + "-vitals-" + i);
927        obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-vitalsigns-dafvitalsigns");
928        i++;
929        obs.setSubject(context.getSubjectRef());
930        obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
931        obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), null));
932
933        boolean found = false;
934        for (Element e : cda.getChildren(o, "id")) {
935          Identifier id = convert.makeIdentifierFromII(e);
936          obs.getIdentifier().add(id);
937        }
938
939        if (!found) {
940          list.addEntry().setItem(new Reference().setReference("Observation/" + obs.getId()));
941          obs.setStatus(ObservationStatus.FINAL);
942          obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime")));
943          String v = cda.getChild(o, "value").getAttribute("value");
944          if (!Utilities.isDecimal(v, true)) {
945            obs.setDataAbsentReason(inspectCode(new CodeableConcept().setText(v), null));
946          } else
947            obs.setValue(convert.makeQuantityFromPQ(cda.getChild(o, "value")));
948          saveResource(obs, "-vs");
949        }
950      }
951    }
952    saveResource(list, "-vs");
953  }
954
955  private void processResultsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
956    scanSection("Results", section);
957
958    ListResource list = new ListResource();
959    list.setId(context.getBaseId() + "-list-results");
960    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafresultlist");
961    list.setSubject(context.getSubjectRef());
962    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
963    list.setTitle(cda.getChild(section, "title").getTextContent());
964    list.setStatus(ListStatus.CURRENT);
965    list.setMode(ListMode.SNAPSHOT);
966    list.setDateElement(context.getNow());
967    list.setSource(context.getAuthorRef());
968    buildNarrative(list, cda.getChild(section, "text"));
969
970    context.setObsId(0);
971    for (Element c : cda.getChildren(section, "entry")) {
972      Element org = cda.getChild(c, "organizer");
973      if (org != null) {
974        Observation panel = new Observation();
975        panel.setId(context.getBaseId() + "-results-" + context.getObsId());
976        panel.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobspanel");
977        context.setObsId(context.getObsId() + 1);
978        panel.setSubject(context.getSubjectRef());
979        panel.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
980        panel.setStatus(ObservationStatus.FINAL);
981        boolean found = false;
982        for (Element e : cda.getChildren(org, "id")) {
983          Identifier id = convert.makeIdentifierFromII(e);
984          panel.getIdentifier().add(id);
985        }
986        if (!found) {
987          list.addEntry().setItem(new Reference().setReference("Observation/" + panel.getId()));
988
989          panel.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(org, "code")), null));
990          for (Element comp : cda.getChildren(org, "component")) {
991            Observation obs = processObservation(cda, convert, context, cda.getChild(comp, "observation"));
992            panel.addRelated().setType(ObservationRelationshipType.HASMEMBER).setTarget(new Reference().setReference("Observation/" + obs.getId()));
993            if (!panel.hasEffective())
994              panel.setEffective(obs.getEffective());
995            else {
996              if (!Base.compareDeep(panel.getEffective(), obs.getEffective(), false)) {
997                Period p = panel.getEffective() instanceof Period ? panel.getEffectivePeriod() : new Period().setStartElement(panel.getEffectiveDateTimeType()).setEndElement(panel.getEffectiveDateTimeType());
998                if (p.getStartElement().after(obs.getEffectiveDateTimeType()))
999                  p.setStartElement(obs.getEffectiveDateTimeType());
1000                if (p.getEndElement().before(obs.getEffectiveDateTimeType()))
1001                  p.setEndElement(obs.getEffectiveDateTimeType());
1002                panel.setEffective(p);
1003              }
1004            }
1005
1006          }
1007          saveResource(panel, "-res");
1008        }
1009      }
1010      Element o = cda.getChild(c, "observation");
1011      if (o != null) {
1012        Observation obs = processObservation(cda, convert, context, o);
1013        list.addEntry().setItem(new Reference().setReference("Observation/" + obs.getId()));
1014      }
1015    }
1016    saveResource(list, "-res");
1017  }
1018
1019  private Observation processObservation(CDAUtilities cda, Convert convert, Context context, Element o) throws Exception {
1020    Observation obs = new Observation();
1021    obs.setId(context.getBaseId() + "-results-" + context.getObsId());
1022    context.setObsId(context.getObsId() + 1);
1023    obs.setSubject(context.getSubjectRef());
1024    obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
1025    obs.setStatus(ObservationStatus.FINAL);
1026    obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime")));
1027    obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), null));
1028    obs.setInterpretation(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "interpretationCode")), null));
1029    Element rr = cda.getChild(o, "referenceRange");
1030    if (rr != null)
1031      obs.addReferenceRange().setText(cda.getChild(cda.getChild(rr, "observationRange"), "text").getTextContent());
1032
1033    Element v = cda.getChild(o, "value");
1034    String type = v.getAttribute("xsi:type");
1035    if ("ST".equals(type)) {
1036      obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobsother");
1037      obs.setValue(new StringType(v.getTextContent()));
1038    } else if ("CD".equals(type)) {
1039      obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobscode");
1040      obs.setValue(inspectCode(convert.makeCodeableConceptFromCD(v), null));
1041    } else if ("PQ".equals(type)) {
1042      obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobsquantity");
1043      String va = cda.getChild(o, "value").getAttribute("value");
1044      if (!Utilities.isDecimal(va, true)) {
1045        obs.setDataAbsentReason(inspectCode(new CodeableConcept().setText(va), null));
1046      } else
1047        obs.setValue(convert.makeQuantityFromPQ(cda.getChild(o, "value"), null));
1048    } else
1049      throw new Exception("Unknown type '" + type + "'");
1050
1051    for (Element e : cda.getChildren(o, "id")) {
1052      Identifier id = convert.makeIdentifierFromII(e);
1053      obs.getIdentifier().add(id);
1054    }
1055    saveResource(obs, "-gen");
1056    return obs;
1057  }
1058
1059  private void processSocialHistorySection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
1060    scanSection("Social History", section);
1061    int i = 0;
1062    for (Element c : cda.getChildren(section, "entry")) {
1063      Element o = cda.getChild(c, "observation");
1064      Observation obs = new Observation();
1065      obs.setId(context.getBaseId() + "-smoking-" + (i == 0 ? "" : Integer.toString(i)));
1066      obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-smokingstatus-dafsmokingstatus");
1067      i++;
1068      obs.setSubject(context.getSubjectRef());
1069      obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
1070      obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), new Coding().setSystem("http://loinc.org").setCode("72166-2")));
1071
1072      boolean found = false;
1073      for (Element e : cda.getChildren(o, "id")) {
1074        Identifier id = convert.makeIdentifierFromII(e);
1075        obs.getIdentifier().add(convert.makeIdentifierFromII(e));
1076      }
1077      if (!found) {
1078        obs.setStatus(ObservationStatus.FINAL);
1079        obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime")));
1080        obs.setValue(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "value")), null));
1081        saveResource(obs, "-sh");
1082      }
1083    }
1084  }
1085
1086  private void processMedicationsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
1087    scanSection("Medications", section);
1088    ListResource list = new ListResource();
1089    list.setId(context.getBaseId() + "-list-medications");
1090    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafmedicationlist");
1091    list.setSubject(context.getSubjectRef());
1092    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
1093    list.setTitle(cda.getChild(section, "title").getTextContent());
1094    list.setStatus(ListStatus.CURRENT);
1095    list.setMode(ListMode.SNAPSHOT);
1096    list.setDateElement(context.getNow());
1097    list.setSource(context.getAuthorRef());
1098    buildNarrative(list, cda.getChild(section, "text"));
1099
1100    int i = 0;
1101    for (Element c : cda.getChildren(section, "entry")) {
1102      Element sa = cda.getChild(c, "substanceAdministration"); // allergy problem act
1103      MedicationStatement ms = new MedicationStatement();
1104      ms.setId(context.getBaseId() + "-medication-" + i);
1105      ms.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/medicationstatement-daf-dafmedicationstatement");
1106      i++;
1107      ms.setSubject(context.getSubjectRef());
1108
1109      boolean found = false;
1110      for (Element e : cda.getChildren(sa, "id")) {
1111        Identifier id = convert.makeIdentifierFromII(e);
1112        ms.getIdentifier().add(id);
1113      }
1114      if (!found) {
1115        ms.setStatus(MedicationStatementStatus.COMPLETED);
1116        list.addEntry().setItem(new Reference().setReference("MedicationStatement/" + ms.getId()));
1117
1118        Element mm = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturedMaterial"); // allergy observation
1119        ms.setMedication(new Reference().setReference("#med"));
1120        Medication med = new Medication();
1121        med.setId("med");
1122        med.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(mm, "code")), null));
1123        ms.getContained().add(med);
1124        Dosage dosage = ms.addDosage();
1125        Element qty = cda.getChild(sa, "doseQuantity"); // allergy observation
1126        try {
1127          if (cda.getChild(qty, "low") != null) {
1128            // todo: this is not correct?
1129            dosage.getExtension().add(new Extension().setUrl("http://healthintersections.com.au/fhir/extensions/medication-statement-range").setValue(convert.makeRangeFromIVLPQ(qty)));
1130          } else {
1131            dosage.setDose(convert.makeQuantityFromPQ(qty));
1132          }
1133        } catch (Exception e) {
1134          System.out.println("  invalid dose quantity '" + qty.getAttribute("value") + " " + qty.getAttribute("unit") + "' (" + e.getClass().getName() + ") in " + context.getBaseId());
1135        }
1136        dosage.setRoute(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sa, "routeCode")), null));
1137        Type t = convert.makeSomethingFromGTS(cda.getChildren(sa, "effectiveTime"));
1138        if (t instanceof Timing) {
1139          dosage.setTiming((Timing) t);
1140          if (dosage.getTiming().hasRepeat() && dosage.getTiming().getRepeat().hasBounds())
1141            ms.setEffective(dosage.getTiming().getRepeat().getBounds());
1142        } else if (t instanceof Period)
1143          ms.setEffective(t);
1144        else
1145          throw new Exception("Undecided how to handle " + t.getClass().getName());
1146
1147        for (Element e : cda.getChildren(sa, "author")) {
1148          if (ms.hasInformationSource())
1149            throw new Error("additional author discovered");
1150          Practitioner p = processPerformer(cda, convert, context, e, "assignedAuthor", "assignedPerson");
1151          Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"));
1152          ms.setInformationSource(ref);
1153          ms.setDateAssertedElement(convert.makeDateTimeFromTS(cda.getChild(e, "time")));
1154        }
1155        saveResource(ms);
1156      }
1157    }
1158    saveResource(list);
1159  }
1160
1161  private void processEncountersSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
1162    scanSection("Encounters", section);
1163    ListResource list = new ListResource();
1164    list.setId(context.getBaseId() + "-list-encounters");
1165    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafencounterlist");
1166    list.setSubject(context.getSubjectRef());
1167    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
1168    list.setTitle(cda.getChild(section, "title").getTextContent());
1169    list.setStatus(ListStatus.CURRENT);
1170    list.setMode(ListMode.SNAPSHOT);
1171    list.setDateElement(context.getNow());
1172    list.setSource(context.getAuthorRef());
1173    buildNarrative(list, cda.getChild(section, "text"));
1174
1175    int i = 0;
1176    for (Element c : cda.getChildren(section, "entry")) {
1177      Element ee = cda.getChild(c, "encounter"); // allergy problem act
1178      Encounter enc = new Encounter();
1179      enc.setId(context.getBaseId() + "-encounter-" + i);
1180      enc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/encounter-daf-dafencounter");
1181      i++;
1182      enc.setSubject(context.getSubjectRef());
1183      list.addEntry().setItem(new Reference().setReference("Encounter/" + enc.getId()));
1184
1185      for (Element e : cda.getChildren(ee, "id"))
1186        enc.getIdentifier().add(convert.makeIdentifierFromII(e));
1187      checkGenerateIdentifier(enc.getIdentifier(), enc);
1188
1189
1190      enc.setPeriod(convert.makePeriodFromIVL(cda.getChild(ee, "effectiveTime")));
1191      if (enc.getPeriod().hasEnd())
1192        enc.setStatus(EncounterStatus.FINISHED);
1193      else
1194        enc.setStatus(EncounterStatus.INPROGRESS);
1195
1196      if (cda.getChild(ee, "text") != null)
1197        enc.setClass_(convertTextToCoding(cda.getChild(ee, "text").getTextContent().trim()));
1198      else
1199        enc.setClass_(null); // todo: fix this
1200
1201      CodeableConcept type = inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(ee, "code")), null);
1202      enc.addType(type);
1203
1204      for (Element e : cda.getChildren(ee, "performer")) {
1205        Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson");
1206        Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"));
1207        enc.addParticipant().setIndividual(ref).setPeriod(convert.makePeriodFromIVL(cda.getChild(e, "time")));
1208      }
1209      enc.addLocation().setLocation(new Reference().setReference("#loc"));
1210      Location loc = new Location();
1211      loc.setId("loc");
1212      Element pr = cda.getChild(cda.getChild(ee, "participant"), "participantRole");
1213      loc.setName(cda.getChild(cda.getChild(pr, "playingEntity"), "name").getTextContent());
1214      loc.setType(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(pr, "code")), null));
1215      enc.getContained().add(loc);
1216      saveResource(enc);
1217    }
1218    saveResource(list);
1219  }
1220
1221  private Coding convertTextToCoding(String v) {
1222    v = v.toLowerCase();
1223    if (v.equals("inpatient"))
1224      return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("IMP");
1225    if (v.equals("emergency department") || v.equals("emergency department admit decision"))
1226      return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("EMER");
1227    if (v.equals("x-ray exam"))
1228      return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB");
1229    if (v.equals("outpatient"))
1230      return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB");
1231    throw new Error("unknown encounter type " + v);
1232  }
1233
1234  private void processImmunizationsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception {
1235    scanSection("Immunizations", section);
1236    ListResource list = new ListResource();
1237    list.setId(context.getBaseId() + "-list-immunizations");
1238    list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafimmunizationlist");
1239    list.setSubject(context.getSubjectRef());
1240    list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null));
1241    list.setTitle(cda.getChild(section, "title").getTextContent());
1242    list.setStatus(ListStatus.CURRENT);
1243    list.setMode(ListMode.SNAPSHOT);
1244    list.setDateElement(context.getNow());
1245    list.setSource(context.getAuthorRef());
1246    buildNarrative(list, cda.getChild(section, "text"));
1247
1248    int i = 0;
1249    for (Element c : cda.getChildren(section, "entry")) {
1250      Element sa = cda.getChild(c, "substanceAdministration"); // allergy problem act
1251      Immunization imm = new Immunization();
1252      imm.setId(context.getBaseId() + "-immunization-" + i);
1253      imm.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/immunization-daf-dafimmunization");
1254      i++;
1255      imm.setPatient(context.getSubjectRef());
1256      imm.setEncounter(new Reference().setReference("Encounter/" + context.getEncounter().getId()));
1257      imm.setNotGiven("true".equals(sa.getAttribute("negationInd")));
1258      imm.setStatus(convertImmunizationStatus(cda.getChild(sa, "statusCode")));
1259      boolean found = false;
1260      for (Element e : cda.getChildren(sa, "id")) {
1261        Identifier id = convert.makeIdentifierFromII(e);
1262        imm.getIdentifier().add(id);
1263      }
1264      if (!found) {
1265        list.addEntry().setItem(new Reference().setReference("Immunization/" + imm.getId()));
1266
1267        imm.setDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(sa, "effectiveTime"), "low")));
1268        if (imm.getNotGiven()) {
1269          Element reason = cda.getChild(cda.getChildByAttribute(sa, "entryRelationship", "typeCode", "RSON"), "observation");
1270          imm.setExplanation(new ImmunizationExplanationComponent());
1271          imm.getExplanation().addReasonNotGiven(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(reason, "code")), null));
1272        }
1273        Element mm = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturedMaterial");
1274        imm.setVaccineCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(mm, "code")), null));
1275        imm.setRoute(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sa, "routeCode")), null));
1276        if (cda.getChild(mm, "lotNumberText") != null)
1277          imm.setLotNumber(cda.getChild(mm, "lotNumberText").getTextContent());
1278        Element mr = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturerOrganization");
1279        if (mr != null)
1280          imm.setManufacturer(new Reference().setDisplay(cda.getChild(mr, "name").getTextContent()));
1281
1282        // the problem with this is that you can't have just a dose sequence number
1283        //                      Element subject = cda.getChild(cda.getChildByAttribute(sa, "entryRelationship", "typeCode", "SUBJ"), "observation");
1284        //                      if (subject != null)
1285        //                              imm.addVaccinationProtocol().setDoseSequence(Integer.parseInt(cda.getChild(subject, "value").getAttribute("value")));
1286
1287        boolean hasprf = false;
1288        for (Element e : cda.getChildren(sa, "performer")) {
1289          if (imm.hasPractitioner())
1290            throw new Error("additional performer discovered");
1291          Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson");
1292          Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"));
1293          imm.addPractitioner().setActor(ref).setRole(new org.hl7.fhir.dstu3.model.CodeableConcept().addCoding(new Coding().setSystem("http://hl7.org/fhir/v2/0443").setCode("AP")));
1294          hasprf = true;
1295        }
1296        imm.setPrimarySource(hasprf);
1297        saveResource(imm);
1298      }
1299    }
1300    saveResource(list);
1301  }
1302
1303  private ImmunizationStatus convertImmunizationStatus(Element child) {
1304    String s = child.getAttribute("code");
1305    if (s.equals("completed"))
1306      return ImmunizationStatus.COMPLETED;
1307    throw new Error("Unexpected status " + s);
1308  }
1309
1310  //  /informationRecipient: 2979
1311  //  /informationRecipient/intendedRecipient: 2979
1312  //  /informationRecipient/intendedRecipient/addr: 2979
1313  //  /informationRecipient/intendedRecipient/informationRecipient: 2979
1314  //  /informationRecipient/intendedRecipient/informationRecipient/name: 2979
1315  //  /informationRecipient/intendedRecipient/receivedOrganization: 2979
1316  //  /informationRecipient/intendedRecipient/receivedOrganization/addr: 2979
1317  //  /informationRecipient/intendedRecipient/receivedOrganization/id: 2979
1318  //  /informationRecipient/intendedRecipient/receivedOrganization/name: 2979
1319  //  /informationRecipient/intendedRecipient/receivedOrganization/telecom: 2979
1320
1321  private void makeBinary(String sourceFolder, String filename, Context context) throws Exception {
1322    Binary binary = new Binary();
1323    binary.setId(context.getBaseId() + "-binary");
1324    binary.setContentType("application/hl7-v3+xml");
1325    binary.setContent(IOUtils.toByteArray(ManagedFileAccess.inStream(Utilities.path(sourceFolder, filename))));
1326    saveResource(binary);
1327  }
1328
1329  private void makeDocumentReference(CDAUtilities cda, Convert convert, Element doc, Context context) throws Exception {
1330    scanSection("document", doc);
1331    DocumentReference ref = new DocumentReference();
1332    ref.setId(context.getBaseId() + "-document");
1333    ref.setMasterIdentifier(convert.makeIdentifierFromII(cda.getChild(doc, "id")));
1334    ref.setSubject(context.getSubjectRef());
1335    ref.setType(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(doc, "code")), null));
1336    ref.addAuthor(context.getAuthorRef());
1337    ref.setCreatedElement(convert.makeDateTimeFromTS(cda.getChild(doc, "effectiveTime")));
1338    ref.setIndexedElement(InstantType.now());
1339    ref.setStatus(DocumentReferenceStatus.CURRENT);
1340    ref.addSecurityLabel(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(doc, "confidentialityCode")), null));
1341    DocumentReferenceContentComponent cnt = ref.addContent();
1342    cnt.getAttachment().setContentType("application/hl7-v3+xml").setUrl("Binary/" + context.getBaseId()).setLanguage(convertLanguage(cda.getChild(doc, "language")));
1343    //          for (Element ti : cda.getChildren(doc, "templateId"))
1344    //                  cnt.addFormat().setSystem("urn:oid:1.3.6.1.4.1.19376.1.2.3").setCode(value)("urn:oid:"+ti.getAttribute("root"));
1345    ref.setContext(new DocumentReferenceContextComponent());
1346    ref.getContext().setPeriod(convert.makePeriodFromIVL(cda.getChild(cda.getChild(doc, "serviceEvent"), "effectiveTime")));
1347    for (CodeableConcept cc : context.getEncounter().getType())
1348      ref.getContext().addEvent(cc);
1349    ref.setDescription(cda.getChild(doc, "title").getTextContent());
1350    ref.setCustodian(new Reference().setReference("Organization/" + processOrganization(cda.getDescendent(doc, "custodian/assignedCustodian/representedCustodianOrganization"), cda, convert, context).getId()));
1351    Practitioner p = processPerformer(cda, convert, context, cda.getChild(doc, "legalAuthenticator"), "assignedEntity", "assignedPerson");
1352    ref.setAuthenticator(new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")));
1353    saveResource(ref);
1354  }
1355
1356  private String convertLanguage(Element child) {
1357    if (child == null)
1358      return null;
1359    return child.getAttribute("code");
1360  }
1361
1362  private CodeableConcept makeClassCode(CodeableConcept type, DocumentReference ref) throws Exception {
1363    CodeableConcept res = new CodeableConcept();
1364    String cs = type.getCoding().get(0).getCode();
1365    if (cs.equals("18842-5") || cs.equals("34133-9"))
1366      return type;
1367    else if (cs.equals("34111-5")) {
1368      ref.getFormatCommentsPre().add("The underlying CDA document has the code '34111-5: Evaluation and Management Note' which is incorrect (wrong display/code combination). The type has been preserved even though it's wrong");
1369      res.addCoding().setSystem("http://loinc.org").setCode("34109-9").setDisplay("Evaluation and management note");
1370    }
1371    //          else if (cs.equals("34111-5") || cs.equals("5666"))
1372    //            res.addCoding().setSystem("http://loinc.org").setCode("LP173418-7").setDisplay("Note");
1373    else
1374      throw new Exception("Uncategorised document type code: " + cs + ": " + type.getCoding().get(0).getDisplay());
1375    return res;
1376
1377  }
1378
1379  private void recordProcedureCode(CodeableConcept code) {
1380    for (Coding c : code.getCoding()) {
1381      count(c, procCodes);
1382    }
1383  }
1384
1385  private void count(Coding c, Map<String, Integer> map) {
1386    String s = c.getSystem() + "::" + c.getCode();
1387    if (map.containsKey(s))
1388      map.put(s, map.get(s) + 1);
1389    else
1390      map.put(s, 1);
1391  }
1392
1393  private void recordConditionCode(CodeableConcept code) {
1394    for (Coding c : code.getCoding()) {
1395      count(c, condCodes);
1396    }
1397  }
1398
1399  private void recordAllergyCode(CodeableConcept code) {
1400    for (Coding c : code.getCoding()) {
1401      count(c, allergyCodes);
1402    }
1403  }
1404
1405  private void dumpCodes() {
1406    dump("Procedure Codes", procCodes);
1407    dump("Condition Codes", condCodes);
1408    dump("Allergy Codes", allergyCodes);
1409  }
1410
1411  private void dump(String string, Map<String, Integer> map) {
1412    System.out.println(string);
1413    System.out.println();
1414    for (String s : map.keySet()) {
1415      System.out.println(s + ": " + map.get(s));
1416    }
1417    System.out.println();
1418    System.out.println();
1419  }
1420
1421}