001package org.hl7.fhir.r5.context;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.HashSet;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009
010import org.hl7.fhir.exceptions.FHIRException;
011import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
012import org.hl7.fhir.r5.model.StructureDefinition;
013import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
014import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
015import org.hl7.fhir.r5.utils.UserDataNames;
016import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
017import org.hl7.fhir.utilities.Utilities;
018
019@MarkedToMoveToAdjunctPackage
020public class TypeManager {
021
022  
023  private CanonicalResourceManager<StructureDefinition> structures;
024  private Map<String, Set<StructureDefinition>> typeDefinitions = new HashMap<>();
025  private Map<String, Set<StructureDefinition>> fhirTypeDefinitions = new HashMap<>();
026  private Set<String> primitiveNames = new HashSet<>();
027  private Set<String> dataTypeNames = new HashSet<>();
028  
029  public TypeManager(CanonicalResourceManager<StructureDefinition> structures) {
030    super();
031    this.structures = structures;
032    reload();
033  }
034
035  public void reload() {
036    typeDefinitions.clear();
037    primitiveNames.clear();
038    dataTypeNames.clear();
039    for (CanonicalResourceManager<StructureDefinition>.CachedCanonicalResource<StructureDefinition> cr : structures.getCachedList()) {
040      if (!"constraint".equals(cr.getDerivation())) {
041        see(cr.getResource());
042      }
043    }    
044  }
045
046  protected Iterable<String> getTypeNames() {
047    return typeDefinitions.keySet();
048  }
049
050  public void see(CanonicalResourceProxy r) {
051    if (!"constraint".equals(r.getDerivation())) {
052      see((StructureDefinition) r.getResource());
053    }
054  }
055
056  public void see(StructureDefinition sd) {
057    if (sd.getDerivation() != TypeDerivationRule.CONSTRAINT && (sd.getSourcePackage() == null || !sd.getSourcePackage().isExamplesPackage())) {
058      String type = sd.getType();
059      Set<StructureDefinition> types = typeDefinitions.get(type);
060      if (types == null) {
061        types = new HashSet<>();
062        typeDefinitions.put(type, types);
063      }
064      types.add(sd);
065      if (sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/") || "true".equals(sd.getUserString(UserDataNames.loader_custom_resource))) {
066        types = fhirTypeDefinitions.get(type);
067        if (types == null) {
068          types = new HashSet<>();
069          fhirTypeDefinitions.put(type, types);
070        }
071        types.add(sd);
072      }
073      if (Utilities.isAbsoluteUrl(type)) {
074        type = sd.getTypeTail();
075        types = typeDefinitions.get(type);
076        if (types == null) {
077          types = new HashSet<>();
078          typeDefinitions.put(type, types);
079        }
080        types.add(sd);
081      }
082      if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
083        primitiveNames.add(sd.getType());
084      } else if (sd.getKind() == StructureDefinitionKind.COMPLEXTYPE) {
085        dataTypeNames.add(sd.getType());
086      }
087    }
088  }
089
090
091  public List<StructureDefinition> getDefinitions(String typeName) {
092    List<StructureDefinition> list = new ArrayList<>();
093    Set<StructureDefinition> defined = typeDefinitions.get(typeName);
094    if (defined != null) {
095      list.addAll(defined);
096    }
097    return list;
098  }
099
100  public StructureDefinition fetchTypeDefinition(String typeName) {
101    Set<StructureDefinition> types = typeDefinitions.get(typeName);
102    if (types == null) {
103      return null; // throw new FHIRException("Unresolved type "+typeName+" (0)");
104    } else if (types.size() == 1) {
105      return types.iterator().next(); 
106    } else { 
107      types = fhirTypeDefinitions.get(typeName);
108      if (types == null) {
109        return null;
110      } else if (types.size() != 1) {
111        throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)");
112      } else  {
113        return types.iterator().next(); 
114      }
115    }
116  }
117
118  public boolean isPrimitive(String type) {
119    if (primitiveNames.contains(type) || Utilities.existsInList(type, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "uuid", "xhtml", "url", "canonical")) {
120      return true;
121    } else {
122      StructureDefinition sd = structures.get(type);
123      return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
124    }
125  }
126
127  public boolean isDataType(String type) {
128    if (dataTypeNames.contains(type) || Utilities.existsInList(type, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing", 
129        "ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext")) {
130      return true;
131    } else {
132      StructureDefinition sd = structures.get(type);
133      return sd != null && sd.getKind() == StructureDefinitionKind.COMPLEXTYPE;      
134    }
135  }
136
137  public void unload() {
138
139    structures.unload();
140    typeDefinitions.clear(); 
141    fhirTypeDefinitions.clear();
142    primitiveNames.clear();
143    dataTypeNames.clear();
144  }
145  
146}