001package org.hl7.fhir.convertors.misc;
002
003
004
005
006
007import java.math.BigDecimal;
008import java.util.HashSet;
009import java.util.List;
010import java.util.Set;
011import java.util.UUID;
012
013/*
014  Copyright (c) 2011+, HL7, Inc.
015  All rights reserved.
016  
017  Redistribution and use in source and binary forms, with or without modification, 
018  are permitted provided that the following conditions are met:
019  
020   * Redistributions of source code must retain the above copyright notice, this 
021     list of conditions and the following disclaimer.
022   * Redistributions in binary form must reproduce the above copyright notice, 
023     this list of conditions and the following disclaimer in the documentation 
024     and/or other materials provided with the distribution.
025   * Neither the name of HL7 nor the names of its contributors may be used to 
026     endorse or promote products derived from this software without specific 
027     prior written permission.
028  
029  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
030  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
031  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
032  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
033  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
034  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
035  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
036  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
037  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
038  POSSIBILITY OF SUCH DAMAGE.
039  
040*/
041
042
043import org.fhir.ucum.UcumService;
044import org.hl7.fhir.dstu3.model.Address;
045import org.hl7.fhir.dstu3.model.Address.AddressUse;
046import org.hl7.fhir.dstu3.model.CodeableConcept;
047import org.hl7.fhir.dstu3.model.Coding;
048import org.hl7.fhir.dstu3.model.ContactPoint;
049import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
050import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointUse;
051import org.hl7.fhir.dstu3.model.DateTimeType;
052import org.hl7.fhir.dstu3.model.DateType;
053import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
054import org.hl7.fhir.dstu3.model.Factory;
055import org.hl7.fhir.dstu3.model.HumanName;
056import org.hl7.fhir.dstu3.model.HumanName.NameUse;
057import org.hl7.fhir.dstu3.model.Identifier;
058import org.hl7.fhir.dstu3.model.InstantType;
059import org.hl7.fhir.dstu3.model.Period;
060import org.hl7.fhir.dstu3.model.Quantity;
061import org.hl7.fhir.dstu3.model.Range;
062import org.hl7.fhir.dstu3.model.SimpleQuantity;
063import org.hl7.fhir.dstu3.model.StringType;
064import org.hl7.fhir.dstu3.model.Timing;
065import org.hl7.fhir.dstu3.model.Timing.EventTiming;
066import org.hl7.fhir.dstu3.model.Timing.TimingRepeatComponent;
067import org.hl7.fhir.dstu3.model.Timing.UnitsOfTime;
068import org.hl7.fhir.dstu3.model.Type;
069import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
070import org.hl7.fhir.utilities.OIDUtils;
071import org.hl7.fhir.utilities.Utilities;
072import org.w3c.dom.Element;
073import org.w3c.dom.Node;
074
075public class Convert {
076
077  private final CDAUtilities cda;
078  private final UcumService ucumSvc;
079  private final Set<String> oids = new HashSet<String>();
080  private final String defaultTimezone;
081  private boolean generateMissingExtensions;
082
083  public Convert(CDAUtilities cda, UcumService ucumSvc, String defaultTimezone) {
084    super();
085    this.cda = cda;
086    this.ucumSvc = ucumSvc;
087    this.defaultTimezone = defaultTimezone;
088  }
089
090  public Identifier makeIdentifierFromII(Element e) throws Exception {
091    Identifier id = new Identifier();
092    String r = e.getAttribute("root");
093    String ex;
094    if (e.hasAttribute("extension") && Utilities.noString(e.getAttribute("extension"))) {
095      if (generateMissingExtensions)
096        ex = UUID.randomUUID().toString();
097      else
098        throw new Exception("Broken identifier - extension is blank");
099    } else
100      ex = e.getAttribute("extension");
101
102    if (Utilities.noString(ex)) {
103      id.setSystem("urn:ietf:rfc:3986");
104      if (isGuid(r))
105        id.setValue("urn:uuid:" + r);
106      else if (UriForOid(r) != null)
107        id.setValue(UriForOid(r));
108      else
109        id.setValue(UriForOid(r));
110    } else {
111      if (isGuid(r))
112        id.setSystem("urn:uuid:" + r);
113      else if (UriForOid(r) != null)
114        id.setSystem(UriForOid(r));
115      else
116        id.setSystem("urn:oid:" + r);
117      id.setValue(ex);
118    }
119    return id;
120  }
121
122  public String makeURIfromII(Element e) {
123    String r = e.getAttribute("root");
124    if (Utilities.noString(e.getAttribute("extension"))) {
125      if (isGuid(r))
126        return "urn:uuid:" + r;
127      else if (UriForOid(r) != null)
128        return UriForOid(r);
129      else
130        return UriForOid(r);
131    } else {
132      if (isGuid(r))
133        return "urn:uuid:" + r + "::" + e.getAttribute("extension");
134      else if (UriForOid(r) != null)
135        return UriForOid(r) + "::" + e.getAttribute("extension");
136      else
137        return "urn:oid:" + r + "::" + e.getAttribute("extension");
138    }
139  }
140
141  private String UriForOid(String r) {
142    String uri = OIDUtils.getUriForOid(r);
143    if (uri != null)
144      return uri;
145    else {
146      oids.add(r);
147      return "urn:oid:" + r;
148    }
149  }
150
151  public boolean isGuid(String r) {
152    return r.matches("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
153  }
154
155  public InstantType makeInstantFromTS(Element child) throws Exception {
156    InstantType i = InstantType.parseV3(child.getAttribute("value"));
157    return i;
158  }
159
160  public CodeableConcept makeCodeableConceptFromCD(Element cv) throws Exception {
161    if (cv == null)
162      return null;
163    CodeableConcept cc = new CodeableConcept();
164    cc.addCoding(makeCodingFromCV(cv));
165    for (Element e : cda.getChildren(cv, "translation"))
166      cc.addCoding(makeCodingFromCV(e));
167    if (cda.getChild(cv, "originalText") != null) {
168      Element ote = cda.getChild(cv, "originalText");
169//                      if (cda.getChild(ote, "reference") != null) {
170//                              if (cda.getChild(ote, "reference").getAttribute("value").startsWith("#")) {
171//                                      Element t = cda.getByXmlId(cda.getChild(ote, "reference").getAttribute("value").substring(1));
172//                                      String ot = t.getTextContent().trim();
173//                                      cc.setText(Utilities.noString(ot) ? null : ot);
174//                              } else
175//                                      throw new Exception("external references not handled yet "+cda.getChild(ote, "reference").getAttribute("value"));
176//                      } else {                        
177      String ot = ote.getTextContent().trim();
178      cc.setText(Utilities.noString(ot) ? null : ot);
179      //}
180    }
181    return cc;
182  }
183
184  public Coding makeCodingFromCV(Element cd) throws Exception {
185    if (cd == null || Utilities.noString(cd.getAttribute("code")))
186      return null;
187    Coding c = new Coding();
188    c.setCode(cd.getAttribute("code"));
189    c.setDisplay(cd.getAttribute("displayName"));
190    String r = cd.getAttribute("codeSystem");
191    String uri = getUriForOID(r);
192    if (uri != null)
193      c.setSystem(uri);
194    else if (isGuid(r))
195      c.setSystem("urn:uuid:" + r);
196    else if (UriForOid(r) != null)
197      c.setSystem(UriForOid(r));
198    else
199      c.setSystem("urn:oid:" + r);
200    return c;
201  }
202
203  private String getUriForOID(String r) {
204    if (r.equals("2.16.840.1.113883.6.1"))
205      return "http://loinc.org";
206    if (r.equals("2.16.840.1.113883.6.96"))
207      return "http://snomed.info/sct";
208    return null;
209  }
210
211  public Address makeAddressFromAD(Element e) {
212    if (e == null)
213      return null;
214    Address a = new Address();
215    String use = e.getAttribute("use");
216    if (use != null) {
217      if (use.equals("H") || use.equals("HP") || use.equals("HV"))
218        a.setUse(AddressUse.HOME);
219      else if (use.equals("WP") || use.equals("DIR") || use.equals("PUB"))
220        a.setUse(AddressUse.WORK);
221      else if (use.equals("TMP"))
222        a.setUse(AddressUse.TEMP);
223      else if (use.equals("BAD"))
224        a.setUse(AddressUse.OLD);
225    }
226    Node n = e.getFirstChild();
227    while (n != null) {
228      if (n.getNodeType() == Node.ELEMENT_NODE) {
229        String v = n.getTextContent();
230        if (n.getLocalName().equals("additionalLocator"))
231          a.getLine().add(makeString(v));
232//                      else if (e.getLocalName().equals("unitID"))
233//                              else if (e.getLocalName().equals("unitType"))
234        else if (n.getLocalName().equals("deliveryAddressLine"))
235          a.getLine().add(makeString(v));
236//                              else if (e.getLocalName().equals("deliveryInstallationType"))
237//                              else if (e.getLocalName().equals("deliveryInstallationArea"))
238//                              else if (e.getLocalName().equals("deliveryInstallationQualifier"))
239//                              else if (e.getLocalName().equals("deliveryMode"))
240//                              else if (e.getLocalName().equals("deliveryModeIdentifier"))
241        else if (n.getLocalName().equals("streetAddressLine"))
242          a.getLine().add(makeString(v));
243//                              else if (e.getLocalName().equals("houseNumber"))
244//                              else if (e.getLocalName().equals("buildingNumberSuffix"))
245//                              else if (e.getLocalName().equals("postBox"))
246//                              else if (e.getLocalName().equals("houseNumberNumeric"))
247//                              else if (e.getLocalName().equals("streetName"))
248//                              else if (e.getLocalName().equals("streetNameBase"))
249//                              else if (e.getLocalName().equals("streetNameType"))
250        else if (n.getLocalName().equals("direction"))
251          a.getLine().add(makeString(v));
252        else if (n.getLocalName().equals("careOf"))
253          a.getLine().add(makeString(v));
254//                              else if (e.getLocalName().equals("censusTract"))
255        else if (n.getLocalName().equals("country"))
256          a.setCountry(v);
257          //else if (e.getLocalName().equals("county"))
258        else if (n.getLocalName().equals("city"))
259          a.setCity(v);
260//                              else if (e.getLocalName().equals("delimiter"))
261//                              else if (e.getLocalName().equals("precinct"))
262        else if (n.getLocalName().equals("state"))
263          a.setState(v);
264        else if (n.getLocalName().equals("postalCode"))
265          a.setPostalCode(v);
266      }
267      n = n.getNextSibling();
268    }
269    return a;
270  }
271
272  public StringType makeString(String v) {
273    StringType s = new StringType();
274    s.setValue(v);
275    return s;
276  }
277
278  public ContactPoint makeContactFromTEL(Element e) throws Exception {
279    if (e == null)
280      return null;
281    if (e.hasAttribute("nullFlavor"))
282      return null;
283    ContactPoint c = new ContactPoint();
284    String use = e.getAttribute("use");
285    if (use != null) {
286      if (use.equals("H") || use.equals("HP") || use.equals("HV"))
287        c.setUse(ContactPointUse.HOME);
288      else if (use.equals("WP") || use.equals("DIR") || use.equals("PUB"))
289        c.setUse(ContactPointUse.WORK);
290      else if (use.equals("TMP"))
291        c.setUse(ContactPointUse.TEMP);
292      else if (use.equals("BAD"))
293        c.setUse(ContactPointUse.OLD);
294    }
295    if (e.getAttribute("value") != null) {
296      String[] url = e.getAttribute("value").split(":");
297      if (url.length == 1) {
298        c.setValue(url[0].trim());
299        c.setSystem(ContactPointSystem.PHONE);
300      } else {
301        if (url[0].equals("tel"))
302          c.setSystem(ContactPointSystem.PHONE);
303        else if (url[0].equals("mailto"))
304          c.setSystem(ContactPointSystem.EMAIL);
305        else if (e.getAttribute("value").contains(":"))
306          c.setSystem(ContactPointSystem.OTHER);
307        else
308          c.setSystem(ContactPointSystem.PHONE);
309        c.setValue(url[1].trim());
310      }
311    }
312    return c;
313
314  }
315
316  public HumanName makeNameFromEN(Element e) {
317    if (e == null)
318      return null;
319    HumanName hn = new HumanName();
320    String use = e.getAttribute("use");
321    if (use != null) {
322      if (use.equals("L"))
323        hn.setUse(NameUse.USUAL);
324      else if (use.equals("C"))
325        hn.setUse(NameUse.OFFICIAL);
326      else if (use.equals("P") || use.equals("A"))
327        hn.setUse(NameUse.ANONYMOUS);
328      else if (use.equals("TMP"))
329        hn.setUse(NameUse.TEMP);
330      else if (use.equals("BAD"))
331        hn.setUse(NameUse.OLD);
332    }
333
334    Node n = e.getFirstChild();
335    while (n != null) {
336      if (n.getNodeType() == Node.ELEMENT_NODE) {
337        String v = n.getTextContent();
338        if (n.getLocalName().equals("family"))
339          hn.setFamilyElement(makeString(v));
340        else if (n.getLocalName().equals("given"))
341          hn.getGiven().add(makeString(v));
342        else if (n.getLocalName().equals("prefix"))
343          hn.getPrefix().add(makeString(v));
344        else if (n.getLocalName().equals("suffix"))
345          hn.getSuffix().add(makeString(v));
346      }
347      n = n.getNextSibling();
348    }
349    return hn;
350  }
351
352  public DateTimeType makeDateTimeFromTS(Element ts) throws Exception {
353    if (ts == null)
354      return null;
355
356    String v = ts.getAttribute("value");
357    if (Utilities.noString(v))
358      return null;
359
360    if (v.length() > 8 && !hasTimezone(v))
361      v += defaultTimezone;
362    DateTimeType d = DateTimeType.parseV3(v);
363    return d;
364  }
365
366  private boolean hasTimezone(String v) {
367    return v.contains("+") || v.contains("-") || v.endsWith("Z");
368  }
369
370
371  public DateType makeDateFromTS(Element ts) throws Exception {
372    if (ts == null)
373      return null;
374
375    String v = ts.getAttribute("value");
376    if (Utilities.noString(v))
377      return null;
378    DateType d = DateType.parseV3(v);
379    return d;
380  }
381
382  public Period makePeriodFromIVL(Element ivl) throws Exception {
383    if (ivl == null)
384      return null;
385    Period p = new Period();
386    Element low = cda.getChild(ivl, "low");
387    if (low != null)
388      p.setStartElement(makeDateTimeFromTS(low));
389    Element high = cda.getChild(ivl, "high");
390    if (high != null)
391      p.setEndElement(makeDateTimeFromTS(high));
392
393    if (p.getStartElement() != null || p.getEndElement() != null)
394      return p;
395    else
396      return null;
397  }
398
399  // this is a weird one - where CDA has an IVL, and FHIR has a date
400  public DateTimeType makeDateTimeFromIVL(Element ivl) throws Exception {
401    if (ivl == null)
402      return null;
403    if (ivl.hasAttribute("value"))
404      return makeDateTimeFromTS(ivl);
405    Element high = cda.getChild(ivl, "high");
406    if (high != null)
407      return makeDateTimeFromTS(high);
408    Element low = cda.getChild(ivl, "low");
409    if (low != null)
410      return makeDateTimeFromTS(low);
411    return null;
412  }
413
414  public Type makeStringFromED(Element e) throws Exception {
415    if (e == null)
416      return null;
417    if (cda.getChild(e, "reference") != null) {
418      if (cda.getChild(e, "reference").getAttribute("value").startsWith("#")) {
419        Element t = cda.getByXmlId(cda.getChild(e, "reference").getAttribute("value").substring(1));
420        String ot = t.getTextContent().trim();
421        return Utilities.noString(ot) ? null : Factory.newString_(ot);
422      } else
423        throw new Exception("external references not handled yet " + cda.getChild(e, "reference").getAttribute("value"));
424    }
425    return Factory.newString_(e.getTextContent());
426  }
427
428  public Type makeTypeFromANY(Element e) throws Exception {
429    if (e == null)
430      return null;
431    String t = e.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "type");
432    if (Utilities.noString(t))
433      throw new Exception("Missing type on RIM attribute with type any");
434    if (t.equals("CD") || t.equals("CE"))
435      return makeCodeableConceptFromCD(e);
436    else if (t.equals("ST"))
437      return makeStringFromED(e);
438    else
439      throw new Exception("Not done yet (type = " + t + ")");
440  }
441
442  public Type makeMatchingTypeFromIVL(Element ivl) throws Exception {
443    if (ivl == null)
444      return null;
445    if (ivl.getAttribute("value") != null)
446      return makeDateTimeFromIVL(ivl);
447    if (cda.getChild(ivl, "low") != null || cda.getChild(ivl, "high") != null)
448      return makePeriodFromIVL(ivl);
449    throw new Exception("not handled yet");
450  }
451
452  public Type makeCodeableConceptFromNullFlavor(String nf) throws Exception {
453    // Some nullFlavors have explicit values in value sets. This can only be called where there aren't.
454    if (nf == null || "".equals(nf))
455      return null;
456    if ("NI".equals(nf))
457      return null; // there's no code for this
458    if ("NA".equals(nf))
459      return Factory.newCodeableConcept("unsupported", "http://hl7.org/fhir/data-absent-reason", "Unsupported"); // todo: is this reasonable? Why else would you use N/A?
460    if ("UNK".equals(nf))
461      return Factory.newCodeableConcept("unknown", "http://hl7.org/fhir/data-absent-reason", "Unknown");
462    if ("ASKU".equals(nf))
463      return Factory.newCodeableConcept("asked", "http://hl7.org/fhir/data-absent-reason", "Asked/Unknown");
464    if ("NAV".equals(nf))
465      return Factory.newCodeableConcept("temp", "http://hl7.org/fhir/data-absent-reason", "Temporarily Unavailable");
466    if ("NASK".equals(nf))
467      return Factory.newCodeableConcept("notasked", "http://hl7.org/fhir/data-absent-reason", "Not Asked");
468    if ("MSK".equals(nf))
469      return Factory.newCodeableConcept("masked", "http://hl7.org/fhir/data-absent-reason", "Masked");
470    if ("OTH".equals(nf))
471      return null; // well, what should be done?
472    return null; // well, what should be done?
473
474  }
475
476  public Range makeRangeFromIVLPQ(Element ivlpq) throws Exception {
477    if (ivlpq == null)
478      return null;
479    Element low = cda.getChild(ivlpq, "low");
480    Element high = cda.getChild(ivlpq, "high");
481    if (low == null && high == null)
482      return null;
483    Range r = new Range();
484    r.setLow(makeSimpleQuantityFromPQ(low, ivlpq.getAttribute("unit")));
485    r.setHigh(makeSimpleQuantityFromPQ(high, ivlpq.getAttribute("unit")));
486    return r;
487  }
488
489  public Quantity makeQuantityFromPQ(Element pq) throws Exception {
490    return makeQuantityFromPQ(pq, null);
491  }
492
493  public Quantity makeQuantityFromPQ(Element pq, String units) throws Exception {
494    if (pq == null)
495      return null;
496    Quantity qty = new Quantity();
497    String n = pq.getAttribute("value").replace(",", "").trim();
498    try {
499      qty.setValue(new BigDecimal(n));
500    } catch (Exception e) {
501      throw new Exception("Unable to process value '" + n + "'", e);
502    }
503    units = Utilities.noString(pq.getAttribute("unit")) ? units : pq.getAttribute("unit");
504    if (!Utilities.noString(units)) {
505      if (ucumSvc == null || ucumSvc.validate(units) != null)
506        qty.setUnit(units);
507      else {
508        qty.setCode(units);
509        qty.setSystem("http://unitsofmeasure.org");
510        qty.setUnit(ucumSvc.getCommonDisplay(units));
511      }
512    }
513    return qty;
514  }
515
516  public SimpleQuantity makeSimpleQuantityFromPQ(Element pq, String units) throws Exception {
517    if (pq == null)
518      return null;
519    SimpleQuantity qty = new SimpleQuantity();
520    String n = pq.getAttribute("value").replace(",", "").trim();
521    try {
522      qty.setValue(new BigDecimal(n));
523    } catch (Exception e) {
524      throw new Exception("Unable to process value '" + n + "'", e);
525    }
526    units = Utilities.noString(pq.getAttribute("unit")) ? units : pq.getAttribute("unit");
527    if (!Utilities.noString(units)) {
528      if (ucumSvc == null || ucumSvc.validate(units) != null)
529        qty.setUnit(units);
530      else {
531        qty.setCode(units);
532        qty.setSystem("http://unitsofmeasure.org");
533        qty.setUnit(ucumSvc.getCommonDisplay(units));
534      }
535    }
536    return qty;
537  }
538
539  public AdministrativeGender makeGenderFromCD(Element cd) throws Exception {
540    String code = cd.getAttribute("code");
541    String system = cd.getAttribute("codeSystem");
542    if ("2.16.840.1.113883.5.1".equals(system)) {
543      if ("F".equals(code))
544        return AdministrativeGender.FEMALE;
545      if ("M".equals(code))
546        return AdministrativeGender.MALE;
547    }
548    throw new Exception("Unable to read Gender " + system + "::" + code);
549  }
550
551  /*
552  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:EIVL_TS]: 389
553  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:EIVL_TS]/event: 389
554  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:IVL_TS]: 33470
555  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:IVL_TS]/high: 20566
556  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:IVL_TS]/high[nullFlavor:NA]: 9581
557  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:IVL_TS]/low: 32501
558  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:IVL_TS]/low[nullFlavor:UNK]: 969
559  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:PIVL_TS]: 17911
560  /entry[COMP]/substanceAdministration[SBADM,EVN]/effectiveTime[type:PIVL_TS]/period: 17911
561   */
562  public Type makeSomethingFromGTS(List<Element> children) throws Exception {
563    if (children.isEmpty())
564      return null;
565    if (children.size() == 1) {
566      String type = children.get(0).getAttribute("xsi:type");
567      if (type.equals("IVL_TS"))
568        return makePeriodFromIVL(children.get(0));
569      else
570        throw new Exception("Unknown GTS type '" + type + "'");
571    }
572    CommaSeparatedStringBuilder t = new CommaSeparatedStringBuilder();
573    for (Element c : children)
574      t.append(c.getAttribute("xsi:type"));
575    if (t.toString().equals("IVL_TS, PIVL_TS"))
576      return makeTimingFromBoundedPIVL(children.get(0), children.get(1));
577    if (t.toString().equals("IVL_TS, EIVL_TS"))
578      return makeTimingFromBoundedEIVL(children.get(0), children.get(1));
579    throw new Exception("Unknown GTS pattern '" + t + "'");
580  }
581
582  private Type makeTimingFromBoundedEIVL(Element ivl, Element eivl) throws Exception {
583    Timing t = new Timing();
584    t.setRepeat(new TimingRepeatComponent());
585    Element e = cda.getChild(eivl, "event");
586    t.getRepeat().setBounds(makePeriodFromIVL(ivl));
587    t.getRepeat().addWhen(convertEventTiming(e.getAttribute("code")));
588    return t;
589  }
590
591  private EventTiming convertEventTiming(String e) throws Exception {
592    if ("HS".equals(e))
593      return EventTiming.HS;
594    throw new Exception("Unknown event " + e);
595  }
596
597  private Timing makeTimingFromBoundedPIVL(Element ivl, Element pivl) throws Exception {
598    Timing t = new Timing();
599    t.setRepeat(new TimingRepeatComponent());
600    Element p = cda.getChild(pivl, "period");
601    t.getRepeat().setBounds(makePeriodFromIVL(ivl));
602    t.getRepeat().setPeriod(new BigDecimal(p.getAttribute("value")));
603    t.getRepeat().setPeriodUnit(convertTimeUnit(p.getAttribute("unit")));
604    return t;
605  }
606
607  private UnitsOfTime convertTimeUnit(String u) throws Exception {
608    if ("h".equals(u))
609      return UnitsOfTime.H;
610    if ("d".equals(u))
611      return UnitsOfTime.D;
612    if ("w".equals(u))
613      return UnitsOfTime.WK;
614    throw new Exception("Unknown unit of time " + u);
615  }
616
617  public Set<String> getOids() {
618    return oids;
619  }
620
621  public boolean isGenerateMissingExtensions() {
622    return generateMissingExtensions;
623  }
624
625  public void setGenerateMissingExtensions(boolean generateMissingExtensions) {
626    this.generateMissingExtensions = generateMissingExtensions;
627  }
628
629
630}