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