All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hl7.fhir.r5.context.ContextUtilities Maven / Gradle / Ivy

package org.hl7.fhir.r5.context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.profile.BindingResolution;
import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType;
import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.UserDataNames;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.NamingSystem;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
import org.hl7.fhir.utilities.OIDUtilities;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;

@MarkedToMoveToAdjunctPackage
public class ContextUtilities implements ProfileKnowledgeProvider {

  private IWorkerContext context;
  private boolean suppressDebugMessages;
  private XVerExtensionManager xverManager;
  private Map oidCache = new HashMap<>();
  private List allStructuresList = new ArrayList();
  private List canonicalResourceNames;
  private List concreteResourceNames;
  private Set concreteResourceNameSet;
  
  public ContextUtilities(IWorkerContext context) {
    super();
    this.context = context;
  }

  public boolean isSuppressDebugMessages() {
    return suppressDebugMessages;
  }

  public void setSuppressDebugMessages(boolean suppressDebugMessages) {
    this.suppressDebugMessages = suppressDebugMessages;
  }
  
  public String oid2Uri(String oid) {
    if (oid != null && oid.startsWith("urn:oid:")) {
      oid = oid.substring(8);
    }
    if (oidCache.containsKey(oid)) {
      return oidCache.get(oid);
    }

    String uri = OIDUtilities.getUriForOid(oid);
    if (uri != null) {
      oidCache.put(oid, uri);
      return uri;
    }
    CodeSystem cs = context.fetchCodeSystem("http://terminology.hl7.org/CodeSystem/v2-tables");
    if (cs != null) {
      for (ConceptDefinitionComponent cc : cs.getConcept()) {
        for (ConceptPropertyComponent cp : cc.getProperty()) {
          if (Utilities.existsInList(cp.getCode(), "v2-table-oid", "v2-cs-oid") && oid.equals(cp.getValue().primitiveValue())) {
            for (ConceptPropertyComponent cp2 : cc.getProperty()) {
              if ("v2-cs-uri".equals(cp2.getCode())) {
                oidCache.put(oid, cp2.getValue().primitiveValue());
                return cp2.getValue().primitiveValue();                  
              }
            }              
          }
        }
      }
    }
    for (CodeSystem css : context.fetchResourcesByType(CodeSystem.class)) {
      if (("urn:oid:"+oid).equals(css.getUrl())) {
        oidCache.put(oid, css.getUrl());
        return css.getUrl();
      }
      for (Identifier id : css.getIdentifier()) {
        if ("urn:ietf:rfc:3986".equals(id.getSystem()) && ("urn:oid:"+oid).equals(id.getValue())) {
          oidCache.put(oid, css.getUrl());
          return css.getUrl();
        }
      }
    }
    for (NamingSystem ns : context.fetchResourcesByType(NamingSystem.class)) {
      if (hasOid(ns, oid)) {
        uri = getUri(ns);
        if (uri != null) {
          oidCache.put(oid, null);
          return null;
        }
      }
    }
    oidCache.put(oid, null);
    return null;
  }    

  private String getUri(NamingSystem ns) {
    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
      if (id.getType() == NamingSystemIdentifierType.URI)
        return id.getValue();
    }
    return null;
  }

  private boolean hasOid(NamingSystem ns, String oid) {
    for (NamingSystemUniqueIdComponent id : ns.getUniqueId()) {
      if (id.getType() == NamingSystemIdentifierType.OID && id.getValue().equals(oid))
        return true;
    }
    return false;
  }

  /**
   * @return a list of the resource and type names defined for this version
   */
  public List getTypeNames() {
    Set result = new HashSet();
    for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
      if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION)
        result.add(sd.getName());
    }
    return Utilities.sorted(result);
  }


  /**
   * @return a set of the resource and type names defined for this version
   */
  public Set getTypeNameSet() {
    Set result = new HashSet();
    for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
      if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && 
          VersionUtilities.versionsCompatible(context.getVersion(), sd.getFhirVersion().toCode())) {
        result.add(sd.getName());
      }
    }
    return result;
  }

  public String getLinkForUrl(String corePath, String url) {
    if (url == null) {
      return null;
    }
    
    if (context.hasResource(CanonicalResource.class, url)) {
      CanonicalResource  cr = context.fetchResource(CanonicalResource.class, url);
      return cr.getWebPath();
    }
    return null;
  }
  

  protected String tail(String url) {
    if (Utilities.noString(url)) {
      return "noname";
    }
    if (url.contains("/")) {
      return url.substring(url.lastIndexOf("/")+1);
    }
    return url;
  }
  
  private boolean hasUrlProperty(StructureDefinition sd) {
    for (ElementDefinition ed : sd.getSnapshot().getElement()) {
      if (ed.getPath().equals(sd.getType()+".url")) {
        return true;
      }
    }
    return false;
  }
  
  // -- profile services ---------------------------------------------------------
  

  /**
   * @return a list of the resource names that are canonical resources defined for this version
   */
  public List getCanonicalResourceNames() {
    if (canonicalResourceNames == null) {
      canonicalResourceNames =  new ArrayList<>();
      Set names = new HashSet<>();
      for (StructureDefinition sd : allStructures()) {
        if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract() && hasUrlProperty(sd)) {
          names.add(sd.getType());
        }
      }
      canonicalResourceNames.addAll(Utilities.sorted(names));
    }
    return canonicalResourceNames;
  }

  /**
   * @return a list of all structure definitions, with snapshots generated (if possible)
   */
  public List allStructures(){
    if (allStructuresList.isEmpty()) {
      Set set = new HashSet();
      for (StructureDefinition sd : getStructures()) {
        if (!set.contains(sd)) {
          try {
            generateSnapshot(sd);
            // new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "snapshot", tail(sd.getUrl())+".xml")), sd);
          } catch (Exception e) {
            if (!isSuppressDebugMessages()) {
              System.out.println("Unable to generate snapshot @2 for "+tail(sd.getUrl()) +" from "+tail(sd.getBaseDefinition())+" because "+e.getMessage());
              if (context.getLogger() != null && context.getLogger().isDebugLogging()) {
                e.printStackTrace();
              }
            }
          }
          allStructuresList.add(sd);
          set.add(sd);
        }
      }
    }
    return allStructuresList;
  }

  /**
   * @return a list of all structure definitions, without trying to generate snapshots
   */
  public List getStructures() {
    return context.fetchResourcesByType(StructureDefinition.class);
  }
    
  /**
   * Given a structure definition, generate a snapshot (or regenerate it)
   * @param p
   * @throws DefinitionException
   * @throws FHIRException
   */
  public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException {
    if ((!p.hasSnapshot() || isProfileNeedsRegenerate(p))) {
      if (!p.hasBaseDefinition())
        throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE___HAS_NO_BASE_AND_NO_SNAPSHOT, p.getName(), p.getUrl()));
      StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getBaseDefinition(), p);
      if (sd == null && "http://hl7.org/fhir/StructureDefinition/Base".equals(p.getBaseDefinition())) {
        sd = ProfileUtilities.makeBaseDefinition(p.getFhirVersion());
      }
      if (sd == null) {
        throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE___BASE__COULD_NOT_BE_RESOLVED, p.getName(), p.getUrl(), p.getBaseDefinition()));
      }
      List msgs = new ArrayList();
      List errors = new ArrayList();
      ProfileUtilities pu = new ProfileUtilities(context, msgs, this);
      pu.setAutoFixSliceNames(true);
      pu.setThrowException(false);
      pu.setForPublication(context.isForPublication());
      if (xverManager == null) {
        xverManager = new XVerExtensionManager(context);
      }
      pu.setXver(xverManager);
      if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
        pu.sortDifferential(sd, p, p.getUrl(), errors, true);
      }
      pu.setDebug(false);
      for (String err : errors) {
        msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getWebPath(), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR));
      }
      pu.generateSnapshot(sd, p, p.getUrl(), sd.getUserString(UserDataNames.render_webroot), p.getName());
      for (ValidationMessage msg : msgs) {
        if ((!ProfileUtilities.isSuppressIgnorableExceptions() && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL) {
          if (!msg.isIgnorableError()) {
            throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE___ELEMENT__ERROR_GENERATING_SNAPSHOT_, p.getName(), p.getUrl(), msg.getLocation(), msg.getMessage()));
          } else {
            System.err.println(msg.getMessage());
          }
        }
      }
      if (!p.hasSnapshot())
        throw new FHIRException(context.formatMessage(I18nConstants.PROFILE___ERROR_GENERATING_SNAPSHOT, p.getName(), p.getUrl()));
      pu = null;
    }
    p.setGeneratedSnapshot(true);
  }
  

  // work around the fact that some Implementation guides were published with old snapshot generators that left invalid snapshots behind.
  private boolean isProfileNeedsRegenerate(StructureDefinition p) {
    boolean needs = !p.hasUserData(UserDataNames.SNAPSHOT_regeneration_tracker) && Utilities.existsInList(p.getUrl(), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse");
    if (needs) {
      p.setUserData(UserDataNames.SNAPSHOT_regeneration_tracker, "yes");
    }
    return needs;
  }

  @Override
  public boolean isPrimitiveType(String type) {
    return context.isPrimitiveType(type);
  }

  @Override
  public boolean isDatatype(String type) {
    StructureDefinition sd = context.fetchTypeDefinition(type);
    return sd != null && (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE || sd.getKind() == StructureDefinitionKind.COMPLEXTYPE) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION;
  }

  @Override
  public boolean isResource(String t) {
    if (getConcreteResourceSet().contains(t)) {
      return true;
    }
    StructureDefinition sd;
    try {
      sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t);
    } catch (Exception e) {
      return false;
    }
    if (sd == null)
      return false;
    if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT)
      return false;
    return sd.getKind() == StructureDefinitionKind.RESOURCE;
  }

  @Override
  public boolean hasLinkFor(String typeSimple) {
    return false;
  }

  @Override
  public String getLinkFor(String corePath, String typeSimple) {
    return null;
  }

  @Override
  public BindingResolution resolveBinding(StructureDefinition profile, ElementDefinitionBindingComponent binding, String path) {
    return null;
  }

  @Override
  public BindingResolution resolveBinding(StructureDefinition profile, String url, String path) {
    return null;
  }

  @Override
  public String getLinkForProfile(StructureDefinition profile, String url) {
    return null;
  }
  @Override
  public boolean prependLinks() {
    return false;
  }

  public StructureDefinition fetchByJsonName(String key) {
    for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
      ElementDefinition ed = sd.getSnapshot().getElementFirstRep();
      if (/*sd.getKind() == StructureDefinitionKind.LOGICAL && */ 
          // this is turned off because it's valid to use a FHIR type directly in
          // an extension of this kind, and that can't be a logical model. Any profile on
          // a type is acceptable as long as it has the json name on it  
          ed != null && ed.hasExtension(ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED) && 
          key.equals(ToolingExtensions.readStringExtension(ed, ToolingExtensions.EXT_JSON_NAME, ToolingExtensions.EXT_JSON_NAME_DEPRECATED))) {
        return sd;
      }
    }
    return null;
  }

  public Set getConcreteResourceSet() {
    if (concreteResourceNameSet == null) {
      concreteResourceNameSet =  new HashSet<>();
      for (StructureDefinition sd : getStructures()) {
        if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract() && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
          concreteResourceNameSet.add(sd.getType());
        }
      }
    }
    return concreteResourceNameSet;
  }

  public List getConcreteResources() {
    if (concreteResourceNames == null) {
      concreteResourceNames =  new ArrayList<>();
      concreteResourceNames.addAll(Utilities.sorted(getConcreteResourceSet()));
    }
    return concreteResourceNames;
  }

  public List listMaps(String url) {
    List res = new ArrayList<>();
    String start = url.substring(0, url.indexOf("*"));
    String end = url.substring(url.indexOf("*")+1);
    for (StructureMap map : context.fetchResourcesByType(StructureMap.class)) {
      String u = map.getUrl();
      if (u.startsWith(start) && u.endsWith(end)) {
        res.add(map);
      }
    }
    return res;
  }

  public List fetchCodeSystemVersions(String system) {
    List res = new ArrayList<>();
    for (CodeSystem cs : context.fetchResourcesByType(CodeSystem.class)) {
      if (system.equals(cs.getUrl()) && cs.hasVersion()) {
        res.add(cs.getVersion());
      }
    }
    return res;
  }

  public StructureDefinition findType(String typeName) {
    StructureDefinition t = context.fetchTypeDefinition(typeName);
    if (t != null) {
      return t;
    }
    List candidates = new ArrayList<>();
    for (StructureDefinition sd : getStructures()) {
      if (sd.getType().equals(typeName)) {
        candidates.add(sd);
      }
    }
    if (candidates.size() == 1) {
      return candidates.get(0);
    }
    return null;
  }

  public StructureDefinition fetchProfileByIdentifier(String tid) {
    for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
      for (Identifier ii : sd.getIdentifier()) {
        if (tid.equals(ii.getValue())) {
          return sd;
        }
      }
    }
    return null;
  }

  public boolean isAbstractType(String typeName) {
    StructureDefinition sd = context.fetchTypeDefinition(typeName);
    if (sd != null) {
      return sd.getAbstract();
    }
    return false;
  }

  public boolean isDomainResource(String typeName) {
    StructureDefinition sd = context.fetchTypeDefinition(typeName);
    while (sd != null) {
      if ("DomainResource".equals(sd.getType())) {
        return true;
      }
      sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());  
    }
    return false;
  }

  public IWorkerContext getWorker() {
    return context;     
  }

  @Override
  public String getCanonicalForDefaultContext() {
    // TODO Auto-generated method stub
    return null;
  }

  public String pinValueSet(String valueSet) {
    return pinValueSet(valueSet, context.getExpansionParameters());
  }

  public String pinValueSet(String value, Parameters expParams) {
    if (value.contains("|")) {
      return value;
    }
    for (ParametersParameterComponent p : expParams.getParameter()) {
      if ("default-valueset-version".equals(p.getName())) {
        String s = p.getValue().primitiveValue();
        if (s.startsWith(value+"|")) {
          return s;
        }
      }
    }
    return value;
  }

  public List allBaseStructures() {
    List res = new ArrayList<>();
    for (StructureDefinition sd : allStructures()) {
      if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() != StructureDefinitionKind.LOGICAL) {
        res.add(sd);
      }
    }
    return res;
  }

  public  List fetchByIdentifier(Class class_, String system) {
    List list = new ArrayList<>();
    for (T t : context.fetchResourcesByType(class_)) {
      if (t instanceof CanonicalResource) {
        CanonicalResource cr = (CanonicalResource) t;
        for (Identifier id : cr.getIdentifier()) {
          if (system.equals(id.getValue())) {
            list.add(t);
          }
        }
      }
    }
    return list;
  }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy