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