diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml
index 3c2f4a34b..164a32acd 100644
--- a/org.hl7.fhir.r5/pom.xml
+++ b/org.hl7.fhir.r5/pom.xml
@@ -50,6 +50,13 @@
true
+
+
+ es.weso
+ shexs_2.12
+ 0.2.31
+
+
com.google.code.gson
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java
index d119aaa97..13a656b7e 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ShExGenerator.java
@@ -31,27 +31,16 @@ package org.hl7.fhir.r5.conformance;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
-import org.hl7.fhir.r5.model.Constants;
-import org.hl7.fhir.r5.model.DataType;
-import org.hl7.fhir.r5.model.DomainResource;
-import org.hl7.fhir.r5.model.ElementDefinition;
-import org.hl7.fhir.r5.model.Enumerations;
-import org.hl7.fhir.r5.model.StructureDefinition;
-import org.hl7.fhir.r5.model.ValueSet;
+import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
+import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.stringtemplate.v4.ST;
public class ShExGenerator {
@@ -59,22 +48,34 @@ public class ShExGenerator {
public enum HTMLLinkPolicy {
NONE, EXTERNAL, INTERNAL
}
- public boolean doDatatypes = true; // add data types
+
+ public enum ConstraintTranslationPolicy {
+ ALL, // Translate all Extensions found; Default (or when no policy defined)
+ GENERIC_ONLY, // Translate all Extensions except constraints with context-of-use
+ CONTEXT_OF_USE_ONLY // Translate only Extensions with context-of-use
+ }
+
+ public boolean doDatatypes = false; // add data types
public boolean withComments = true; // include comments
public boolean completeModel = false; // doing complete build (fhir.shex)
+ public boolean debugMode = false;
+
+ public ConstraintTranslationPolicy constraintPolicy = ConstraintTranslationPolicy.ALL;
private static String SHEX_TEMPLATE = "$header$\n\n" +
- "$shapeDefinitions$";
+
+ "$shapeDefinitions$";
// A header is a list of prefixes, a base declaration and a start node
private static String FHIR = "http://hl7.org/fhir/";
private static String FHIR_VS = FHIR + "ValueSet/";
private static String HEADER_TEMPLATE =
- "PREFIX fhir: <$fhir$> \n" +
- "PREFIX fhirvs: <$fhirvs$>\n" +
- "PREFIX xsd: \n" +
- "BASE \n$start$";
+ "PREFIX fhir: <$fhir$> \n" +
+ "PREFIX fhirvs: <$fhirvs$>\n" +
+ "PREFIX xsd: \n" +
+ "PREFIX rdf: \n" +
+ "BASE \n$start$";
// Start template for single (open) entry
private static String START_TEMPLATE = "\n\nstart=@<$id$> AND {fhir:nodeRole [fhir:treeRoot]}\n";
@@ -93,23 +94,50 @@ public class ShExGenerator {
// the list of element declarations
// an optional index element (for appearances inside ordered lists)
private static String SHAPE_DEFINITION_TEMPLATE =
- "$comment$\n<$id$> CLOSED {\n $resourceDecl$" +
- "\n $elements$" +
- "\n fhir:index xsd:integer? # Relative position in a list\n}\n";
+ "$comment$\n<$id$> CLOSED { $fhirType$ " +
+ "\n $resourceDecl$" +
+ "\n $elements$" +
+ "\n $contextOfUse$" +
+ "\n} $constraints$ \n";
+
+ // Base DataTypes
+ private List baseDataTypes = Arrays.asList(
+ "DataType",
+ "PrimitiveType"
+ );
+
+ private List mappedFunctions = Arrays.asList(
+ "empty",
+ "exists",
+ "hasValue",
+ "matches",
+ "contains",
+ "toString",
+ "is",
+ "where"
+ );
+
+ private static String ONE_OR_MORE_PREFIX = "OneOrMore_";
+ private static String ONE_OR_MORE_CHOICES = "_One-Or-More-Choices_";
+ private static String ONE_OR_MORE_TEMPLATE =
+ "\n$comment$\n<$oomType$> CLOSED {" +
+ "\n rdf:first @<$origType$> $restriction$ ;" +
+ "\n rdf:rest [rdf:nil] OR @<$oomType$> " +
+ "\n}\n";
// Resource Definition
// an open shape of type Resource. Used when completeModel = false.
private static String RESOURCE_SHAPE_TEMPLATE =
- "$comment$\n {a .+;" +
- "\n $elements$" +
- "\n fhir:index xsd:integer?" +
- "\n}\n";
+ "$comment$\n {a .+;" +
+ "\n $elements$" +
+ "\n $contextOfUse$" +
+ "\n} $constraints$ \n";
// If we have knowledge of all of the possible resources available to us (completeModel = true), we can build
// a model of all possible resources.
private static String COMPLETE_RESOURCE_TEMPLATE =
- " @<$resources$>" +
- "\n\n";
+ " @<$resources$>" +
+ "\n\n";
// Resource Declaration
// a type node
@@ -176,18 +204,18 @@ public class ShExGenerator {
// A typed reference -- a fhir:uri with an optional type and the possibility of a resolvable shape
private static String TYPED_REFERENCE_TEMPLATE = "\n<$refType$Reference> CLOSED {" +
- "\n fhir:Element.id @?;" +
- "\n fhir:Element.extension @*;" +
- "\n fhir:link @<$refType$> OR CLOSED {a [fhir:$refType$]}?;" +
- "\n fhir:Reference.reference @?;" +
- "\n fhir:Reference.display @?;" +
- "\n fhir:index xsd:integer?" +
- "\n}";
+ "\n fhir:Element.id @?;" +
+ "\n fhir:Element.extension @*;" +
+ "\n fhir:link @<$refType$> OR CLOSED {a [fhir:$refType$]}?;" +
+ "\n fhir:Reference.reference @?;" +
+ "\n fhir:Reference.display @?;" +
+ // "\n fhir:index xsd:integer?" +
+ "\n}";
private static String TARGET_REFERENCE_TEMPLATE = "\n<$refType$> {" +
- "\n a [fhir:$refType$];" +
- "\n fhir:nodeRole [fhir:treeRoot]?" +
- "\n}";
+ "\n a [fhir:$refType$];" +
+ "\n fhir:nodeRole [fhir:treeRoot]?" +
+ "\n}";
// A value set definition
private static String VALUE_SET_DEFINITION = "# $comment$\n$vsuri$$val_list$\n";
@@ -208,6 +236,13 @@ public class ShExGenerator {
* doDataTypes -- whether or not to emit the data types.
*/
private HashSet> innerTypes, emittedInnerTypes;
+
+ private List oneOrMoreTypes;
+
+ private List constraintsList;
+
+ private List unMappedFunctions;
+
private HashSet datatypes, emittedDatatypes;
private HashSet references;
private LinkedList uniq_structures;
@@ -215,23 +250,43 @@ public class ShExGenerator {
private HashSet required_value_sets;
private HashSet known_resources; // Used when generating a full definition
+ // List of URLs of Excluded Structure Definitions from ShEx Schema generation.
+ private List excludedSDUrls;
+
+ // List of URLs of selected Structure Definitions of Extensions from ShEx Schema generation.
+ // Extensions are Structure Definitions with type as "Extension".
+ private List selectedExtensions;
+ private List selectedExtensionUrls;
+ private FHIRPathEngine fpe;
+
public ShExGenerator(IWorkerContext context) {
super();
this.context = context;
profileUtilities = new ProfileUtilities(context, null, null);
innerTypes = new HashSet>();
+ oneOrMoreTypes = new ArrayList();
+ constraintsList = new ArrayList();
+ unMappedFunctions = new ArrayList();
emittedInnerTypes = new HashSet>();
datatypes = new HashSet();
emittedDatatypes = new HashSet();
references = new HashSet();
required_value_sets = new HashSet();
known_resources = new HashSet();
+ excludedSDUrls = new ArrayList();
+ selectedExtensions = new ArrayList();
+ selectedExtensionUrls = new ArrayList();
+
+ fpe = new FHIRPathEngine(context);
}
public String generate(HTMLLinkPolicy links, StructureDefinition structure) {
List list = new ArrayList();
list.add(structure);
innerTypes.clear();
+ oneOrMoreTypes.clear();
+ constraintsList.clear();
+ unMappedFunctions.clear();
emittedInnerTypes.clear();
datatypes.clear();
emittedDatatypes.clear();
@@ -241,6 +296,29 @@ public class ShExGenerator {
return generate(links, list);
}
+ public List getExcludedStructureDefinitionUrls(){
+ return this.excludedSDUrls;
+ }
+
+ public void setExcludedStructureDefinitionUrls(List excludedSDs){
+ this.excludedSDUrls = excludedSDs;
+ }
+
+ public List getSelectedExtensions(){
+ return this.selectedExtensions;
+ }
+
+ public void setSelectedExtension(List selectedExtensions){
+ this.selectedExtensions = selectedExtensions;
+
+ selectedExtensionUrls.clear();
+
+ for (StructureDefinition eSD : selectedExtensions){
+ if (!selectedExtensionUrls.contains(eSD.getUrl()))
+ selectedExtensionUrls.add(eSD.getUrl());
+ }
+ }
+
public class SortById implements Comparator {
@Override
@@ -262,74 +340,184 @@ public class ShExGenerator {
* @param structures list of structure definitions to render
* @return ShEx definition of structures
*/
- public String generate(HTMLLinkPolicy links, List structures) {
+ public String generate(HTMLLinkPolicy links, List structures, List excludedSDUrls) {
+ this.excludedSDUrls = excludedSDUrls;
+ if ((structures != null )&&(this.selectedExtensions != null)){
+ structures.addAll(this.selectedExtensions);
+ }
+
+ return generate(links, structures);
+ }
+
+ /**
+ * this is called externally to generate a set of structures to a single ShEx file
+ * generally, it will be called with a single structure, or a long list of structures (all of them)
+ *
+ * @param links HTML link rendering policy
+ * @param structures list of structure definitions to render
+ * @return ShEx definition of structures
+ */
+ public String generate(HTMLLinkPolicy links, List structures) {
ST shex_def = tmplt(SHEX_TEMPLATE);
String start_cmd;
if(completeModel || structures.get(0).getKind().equals(StructureDefinition.StructureDefinitionKind.RESOURCE))
-// || structures.get(0).getKind().equals(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE))
start_cmd = completeModel? tmplt(ALL_START_TEMPLATE).render() :
- tmplt(START_TEMPLATE).add("id", structures.get(0).getId()).render();
+ tmplt(START_TEMPLATE).add("id", structures.get(0).getId()).render();
else
start_cmd = "";
- shex_def.add("header", tmplt(HEADER_TEMPLATE).
- add("start", start_cmd).
- add("fhir", FHIR).
- add("fhirvs", FHIR_VS).render());
+
+ shex_def.add("header",
+ tmplt(HEADER_TEMPLATE).
+ add("start", start_cmd).
+ add("fhir", FHIR).
+ add("fhirvs", FHIR_VS).render());
Collections.sort(structures, new SortById());
StringBuilder shapeDefinitions = new StringBuilder();
- // For unknown reasons, the list of structures carries duplicates. We remove them
- // Also, it is possible for the same sd to have multiple hashes...
+ // For unknown reasons, the list of structures carries duplicates.
+ // We remove them. Also, it is possible for the same sd to have multiple hashes...
uniq_structures = new LinkedList();
uniq_structure_urls = new HashSet();
for (StructureDefinition sd : structures) {
+ // Exclusion Criteria...
+ if ((excludedSDUrls != null) &&
+ (excludedSDUrls.contains(sd.getUrl()))) {
+ printBuildMessage("SKIPPED Generating ShEx for " + sd.getName() + " [ " + sd.getUrl() + " ] !");
+ printBuildMessage("Reason: It is in excluded list of structures.");
+ continue;
+ }
+
+ if ("Extension".equals(sd.getType())) {
+ if ((!this.selectedExtensionUrls.isEmpty()) && (!this.selectedExtensionUrls.contains(sd.getUrl()))) {
+ printBuildMessage("SKIPPED Generating ShEx for " + sd.getName() + " [ " + sd.getUrl() + " ] !");
+ printBuildMessage("Reason: It is NOT included in the list of selected extensions.");
+ continue;
+ }
+
+ if ((this.constraintPolicy == ConstraintTranslationPolicy.GENERIC_ONLY) && (sd.hasContext())) {
+ printBuildMessage("SKIPPED Generating ShEx for " + sd.getName() + " [ " + sd.getUrl() + " ] !");
+ printBuildMessage("Reason: ConstraintTranslationPolicy is set to GENERIC_ONLY, and this Structure has Context of Use.");
+ continue;
+ }
+
+ if ((this.constraintPolicy == ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY) && (!sd.hasContext())) {
+ printBuildMessage("SKIPPED Generating ShEx for " + sd.getName() + " [ " + sd.getUrl() + " ] !");
+ printBuildMessage("Reason: ConstraintTranslationPolicy is set to CONTEXT_OF_USE_ONLY, and this Structure has no Context of Use.");
+ continue;
+ }
+ }
+
if (!uniq_structure_urls.contains(sd.getUrl())) {
uniq_structures.add(sd);
uniq_structure_urls.add(sd.getUrl());
}
}
-
+ boolean isShapeDefinitionEmpty = true;
for (StructureDefinition sd : uniq_structures) {
- shapeDefinitions.append(genShapeDefinition(sd, true));
+ printBuildMessage(" ---- Generating ShEx for : " + sd.getName() + " [ " + sd.getUrl() + " ] ...");
+ String shapeDefinitionStr = genShapeDefinition(sd, true);
+
+ if (!shapeDefinitionStr.isEmpty()) {
+ isShapeDefinitionEmpty = false;
+ shapeDefinitions.append(shapeDefinitionStr);
+ }
+ else {
+ printBuildMessage(" ---- EMPTY/No ShEx SCHEMA generated for : " + sd.getName() + " [ " + sd.getUrl() + " ].");
+ }
+ }
+
+ // There was not shape generated. return empty.
+ // No need to generate data types, references and valuesets
+ if (isShapeDefinitionEmpty) {
+ return "";
}
shapeDefinitions.append(emitInnerTypes());
+
+ // If data types are to be put in the same file
if(doDatatypes) {
shapeDefinitions.append("\n#---------------------- Data Types -------------------\n");
while (emittedDatatypes.size() < datatypes.size() ||
- emittedInnerTypes.size() < innerTypes.size()) {
+ emittedInnerTypes.size() < innerTypes.size()) {
shapeDefinitions.append(emitDataTypes());
+ // As process data types, it may introduce some more inner types, so we repeat the call here.
shapeDefinitions.append(emitInnerTypes());
}
}
- shapeDefinitions.append("\n#---------------------- Reference Types -------------------\n");
- for(String r: references) {
- shapeDefinitions.append("\n").append(tmplt(TYPED_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n");
- if (!"Resource".equals(r) && !known_resources.contains(r))
- shapeDefinitions.append("\n").append(tmplt(TARGET_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n");
+ if (oneOrMoreTypes.size() > 0) {
+ shapeDefinitions.append("\n#---------------------- Cardinality Types (OneOrMore) -------------------\n");
+ oneOrMoreTypes.forEach((String oomType) -> {
+ shapeDefinitions.append(getOneOrMoreType(oomType));
+ });
}
+
+ if (references.size() > 0) {
+ shapeDefinitions.append("\n#---------------------- Reference Types -------------------\n");
+ for (String r : references) {
+ shapeDefinitions.append("\n").append(tmplt(TYPED_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n");
+ if (!"Resource".equals(r) && !known_resources.contains(r))
+ shapeDefinitions.append("\n").append(tmplt(TARGET_REFERENCE_TEMPLATE).add("refType", r).render()).append("\n");
+ }
+ }
+
shex_def.add("shapeDefinitions", shapeDefinitions);
if(completeModel && known_resources.size() > 0) {
shapeDefinitions.append("\n").append(tmplt(COMPLETE_RESOURCE_TEMPLATE)
- .add("resources", StringUtils.join(known_resources, "> OR\n\t@<")).render());
+ .add("resources", StringUtils.join(known_resources, "> OR\n\t@<")).render());
List all_entries = new ArrayList();
for(String kr: known_resources)
all_entries.add(tmplt(ALL_ENTRY_TEMPLATE).add("id", kr).render());
shapeDefinitions.append("\n").append(tmplt(ALL_TEMPLATE)
- .add("all_entries", StringUtils.join(all_entries, " OR\n\t")).render());
+ .add("all_entries", StringUtils.join(all_entries, " OR\n\t")).render());
+ }
+
+ if (required_value_sets.size() > 0) {
+ shapeDefinitions.append("\n#---------------------- Value Sets ------------------------\n");
+ for (ValueSet vs : required_value_sets)
+ shapeDefinitions.append("\n").append(genValueSet(vs));
+ }
+
+ if ((unMappedFunctions != null)&&(!unMappedFunctions.isEmpty())) {
+ debug("------------------------- Unmapped Functions ---------------------");
+ for (String um : unMappedFunctions) {
+ debug(um);
+ }
}
- shapeDefinitions.append("\n#---------------------- Value Sets ------------------------\n");
- for(ValueSet vs: required_value_sets)
- shapeDefinitions.append("\n").append(genValueSet(vs));
return shex_def.render();
}
+ private String getExtendedType(StructureDefinition sd){
+ if (sd == null)
+ return null;
+
+ String sId = sd.getId();
+ String bd = "";
+ if (sd.hasBaseDefinition()) {
+ bd = sd.getBaseDefinition();
+ String[] els = bd.split("/");
+ bd = els[els.length - 1];
+
+ sId += "> EXTENDS @<" + bd;
+ }
+
+ return sId;
+ }
+
+ private String getExtendedType(ElementDefinition ed){
+ if (ed == null)
+ return "";
+ String bd = (ed.getType().size() > 0)? (ed.getType().get(0).getCode()) : "";
+ if (bd != null && !bd.isEmpty() && !baseDataTypes.contains(bd)) {
+ bd = "> EXTENDS @<" + bd;
+ }
+ return bd;
+ }
/**
* Emit a ShEx definition for the supplied StructureDefinition
@@ -344,22 +532,21 @@ public class ShExGenerator {
ST shape_defn;
// Resources are either incomplete items or consist of everything that is defined as a resource (completeModel)
+ // if (sd.getName().equals("ActivityDefinition")){
+ // debug("ActivityDefinition found");
+ // }
if("Resource".equals(sd.getName())) {
shape_defn = tmplt(RESOURCE_SHAPE_TEMPLATE);
known_resources.add(sd.getName());
} else {
- shape_defn = tmplt(SHAPE_DEFINITION_TEMPLATE).add("id", sd.getId());
- if (sd.getKind().equals(StructureDefinition.StructureDefinitionKind.RESOURCE)) {
-// || sd.getKind().equals(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE)) {
- known_resources.add(sd.getName());
- ST resource_decl = tmplt(RESOURCE_DECL_TEMPLATE).
- add("id", sd.getId()).
- add("root", tmplt(ROOT_TEMPLATE));
-// add("root", top_level ? tmplt(ROOT_TEMPLATE) : "");
- shape_defn.add("resourceDecl", resource_decl.render());
- } else {
- shape_defn.add("resourceDecl", "");
- }
+ shape_defn = tmplt(SHAPE_DEFINITION_TEMPLATE).add("id", getExtendedType(sd));
+ known_resources.add(sd.getName());
+ ST resource_decl = tmplt(RESOURCE_DECL_TEMPLATE).
+ add("id", sd.getId()).
+ add("root", tmplt(ROOT_TEMPLATE));
+ shape_defn.add("resourceDecl", resource_decl.render());
+
+ shape_defn.add("fhirType", " ");
}
// Generate the defining elements
@@ -373,22 +560,465 @@ public class ShExGenerator {
elements.add(tmplt(CONCEPT_REFERENCES_TEMPLATE).render());
else if (sdn.equals("Reference"))
elements.add(tmplt(RESOURCE_LINK_TEMPLATE).render());
-// else if (sdn.equals("Extension"))
-// return tmplt(EXTENSION_TEMPLATE).render();
String root_comment = null;
+
+ constraintsList.clear();
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if(!ed.getPath().contains("."))
root_comment = ed.getShort();
- else if (StringUtils.countMatches(ed.getPath(), ".") == 1 && !"0".equals(ed.getMax())) {
- elements.add(genElementDefinition(sd, ed));
+ else if (
+ (StringUtils.countMatches(ed.getPath(), ".") == 1 && !"0".equals(ed.getMax()))
+ && (ed.hasBase()
+ && (
+ ed.getBase().getPath().startsWith(sdn)
+ || (ed.getBase().getPath().startsWith("Extension"))
+ || (ed.getBase().getPath().startsWith("Element.extension")&&(ed.hasSliceName()))
+ )
+ )
+ ){
+ String elementDefinition = genElementDefinition(sd, ed);
+
+ boolean isInnerType = false;
+ if (isInInnerTypes(ed)){
+ //debug("This element is already in innerTypes:" + ed.getPath());
+ isInnerType = true;
+ }
+
+ // Process constraints
+ for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) {
+ String sdType = sd.getType();
+ String cstype = constraint.getSource();
+ if ((!cstype.isEmpty()) && (cstype.indexOf("/") != -1)) {
+ String[] els = cstype.split("/");
+ cstype = els[els.length - 1];
+ }
+ // Implement here if SD type == constraint source OR SD type is Primitive or DataType then add constraint to SD otherwise skip it.
+ //if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType) || baseDataTypes.contains(bd)) {
+ //if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) {
+ String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath();
+ String shortId = id.substring(id.lastIndexOf(".") + 1);
+ if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) {
+ if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) {
+ if (!isInnerType) {
+ debug("\n Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression());
+ String transl = translateConstraint(sd, ed, constraint);
+ if (transl.isEmpty() || constraintsList.contains(transl))
+ continue;
+ constraintsList.add(transl);
+ }
+ }
+ }
+ }
+ elements.add(elementDefinition);
+ }
+ else {
+ List children = profileUtilities.getChildList(sd, ed);
+ if (children.size() > 0) {
+ for (ElementDefinition child : children) {
+ if (child.getPath().startsWith(ed.getPath()))
+ innerTypes.add(new ImmutablePair(sd, ed));
+ }
+ }
+ }
+ }
+
+ // Constraints for differential to cover constraints on SD itself without any elements of its own
+ for (ElementDefinition ded : sd.getDifferential().getElement()) {
+ // Process constraints
+ for (ElementDefinition.ElementDefinitionConstraintComponent dconstraint : ded.getConstraint()) {
+ String sdType = sd.getType();
+
+ String id = ded.hasBase() ? ded.getBase().getPath() : ded.getPath();
+ String shortId = id.substring(id.lastIndexOf(".") + 1);
+
+ if (!isInInnerTypes(ded)) {
+ debug("\n Key: " + dconstraint.getKey() + " SD type: " + sd.getType() + " Element: " + ded.getPath() + " Constraint Source: " + dconstraint.getSource() + " Constraint:" + dconstraint.getExpression());
+ String dtransl = translateConstraint(sd, ded, dconstraint);
+ if (dtransl.isEmpty() || constraintsList.contains(dtransl))
+ continue;
+ constraintsList.add(dtransl);
+ }
}
}
shape_defn.add("elements", StringUtils.join(elements, "\n"));
shape_defn.add("comment", root_comment == null? " " : "# " + root_comment);
+
+ String constraintStr = "";
+
+ if (!constraintsList.isEmpty()) {
+ constraintStr = "AND (\n\n" + StringUtils.join(constraintsList, "\n\n) AND (\n\n") + "\n\n)\n";
+ }
+
+ shape_defn.add("constraints", constraintStr);
+
+ String contextOfUseStr = "";
+ ArrayList contextOfUse = new ArrayList();
+ if (!sd.getContext().isEmpty()) {
+ for (StructureDefinition.StructureDefinitionContextComponent uc : sd.getContext()) {
+ if (!uc.getExpression().isEmpty()) {
+ String toStore = uc.getExpression() ;
+ String[] backRefs = toStore.split("\\.");
+ toStore = "a [fhir:" + backRefs[0] + "]";
+ for (int i = 1; i < backRefs.length; i++)
+ toStore = "^fhir:" + backRefs[i] + " {" + toStore + "}";
+
+ if (!contextOfUse.contains(toStore)) {
+ contextOfUse.add(toStore);
+ }
+ }
+ }
+ contextOfUseStr = "^fhir:extension { " + StringUtils.join(contextOfUse, " OR \n\t\t\t\t") + "\n\t\t}";
+ }
+
+ shape_defn.add("contextOfUse", contextOfUseStr);
+
return shape_defn.render();
}
+ /**
+ * @param ed
+ * @param constraint
+ * @return
+ */
+ private String translateConstraint(StructureDefinition sd, ElementDefinition ed, ElementDefinition.ElementDefinitionConstraintComponent constraint){
+ String translated = "";
+
+ if (constraint != null) {
+ String ce = constraint.getExpression();
+ String constItem = "FHIR-SD-Path:" + ed.getPath() + " Expression: " + ce;
+ try {
+ translated = "# Constraint: UniqueKey:" + constraint.getKey() + "\n# Human readable:" + constraint.getHuman() + "\n# Constraint:" + constraint.getExpression() + "\n# ShEx:\n";
+
+ ExpressionNode expr = fpe.parse(ce);
+ String shexConstraint = processExpressionNode(sd, ed, expr, false, 0);
+ shexConstraint = shexConstraint.replaceAll("CALLER", "");
+ debug(" Parsed to ShEx Constraint:" + shexConstraint);
+ if (!shexConstraint.isEmpty())
+ translated += "\n" + shexConstraint;
+
+ debug(" TRANSLATED\t"+ed.getPath()+"\t"+constraint.getHuman()+"\t"+constraint.getExpression()+"\t"+shexConstraint);
+
+ } catch (Exception e) {
+ String message = " FAILED to parse the constraint: " + constItem + " [ " + e.getMessage() + " ]";
+ // Now make this a comment so that it does not fail when schema is resolved in validator
+ // TODO: This needs to be fixed
+ // TODO: it should be
+ // translated = message
+
+ translated = "";
+ debug(message);
+ }
+ }
+ return translated;
+ }
+
+ /**
+ * @param node
+ * @param quote
+ * @return
+ */
+ private String processExpressionNode(StructureDefinition sd, ElementDefinition ed, ExpressionNode node, boolean quote, int depth) {
+ if (node == null)
+ return "";
+ boolean toQuote = quote;
+
+ String innerShEx = processExpressionNode(sd, ed, node.getInner(), quote, depth + 1);
+
+ String translatedShEx = "";
+
+ boolean treatBothOpsSame = false;
+ // Figure out if there are any operations defined on this node
+ String ops = "";
+ String endOps = "";
+ if (node.getOperation() != null) {
+ String opName = node.getOperation().name();
+ switch (opName) {
+ case "Or":
+ ops = " OR ";
+ break;
+ case "Union":
+ ops = " | ";
+ break;
+ case "In" :
+ case "Equals":
+ case "Contains":
+ ops = " { fhir:v [";
+ endOps = "] } ";
+ toQuote = true;
+ break;
+ case "NotEquals":
+ ops = " [fhir:v . -";
+ endOps = "] ";
+ toQuote = true;
+ break;
+ case "Greater":
+ if (node.getOpNext().getKind().equals(ExpressionNode.Kind.Constant)) {
+ ops = " { fhir:v MinExclusive ";
+ endOps = " } ";
+ } else {
+ String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind();
+ addUnmappedFunction(opName);
+ ops = TBD(opName);
+ }
+ break;
+ case "GreaterOrEqual":
+ if (node.getOpNext().getKind().equals(ExpressionNode.Kind.Constant)) {
+ ops = " { fhir:v MinInclusive ";
+ endOps = " } ";
+ } else {
+ String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind();
+ addUnmappedFunction(opName);
+ ops = TBD(opName);
+ }
+ break;
+ case "Less":
+ case "LessThan":
+ if (node.getOpNext().getKind().equals(ExpressionNode.Kind.Constant)) {
+ ops = " { fhir:v MaxExclusive ";
+ endOps = " } ";
+ } else {
+ String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind();
+ addUnmappedFunction(opName);
+ ops = TBD(opName);
+ }
+ break;
+ case "LessOrEqual":
+ if (node.getOpNext().getKind().equals(ExpressionNode.Kind.Constant)) {
+ ops = " { fhir:v MaxInclusive ";
+ endOps = " } ";
+ } else {
+ String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind();
+ addUnmappedFunction(opName);
+ ops = TBD(opName);
+ }
+ break;
+ case "And":
+ case "Implies" :
+ ops = " AND ";
+ break;
+ case "As":
+ case "Is":
+ ops = " a ";
+ break;
+ case "Xor":
+ ops = " XOR ";
+ break;
+ default:
+ String toStore = "UNMAPPED_OPERATOR_" + opName + " in Node type: " + node.getKind();
+ if (!unMappedFunctions.contains(toStore))
+ unMappedFunctions.add(toStore);
+
+ ops = TBD(opName);
+ }
+ }
+
+ // Functions
+ String fExp = "";
+ String pExp = "";
+ boolean isFunctionCall = false;
+ ExpressionNode.Kind kind = node.getKind();
+ if (kind == ExpressionNode.Kind.Function) {
+ String funcName = node.getName();
+ if (!mappedFunctions.contains(funcName)) {
+ if (node.parameterCount() == 0) {
+ if ("not".equals(funcName))
+ fExp = " NOT { CALLER }";
+ else {
+ fExp = " " + funcName + "( CALLER )";
+
+ addUnmappedFunction(node.getFunction().toCode());
+ String toStore = addUnmappedFunction(node.getFunction().toCode());
+ }
+ }
+ }
+
+ if ("".equals(fExp)) {
+ switch (funcName) {
+ case "empty":
+ //fExp = " .?";
+ fExp = " NOT { CALLER {fhir:v .} } " ;
+ break;
+ case "exists":
+ case "hasValue":
+ fExp = " .";
+ break;
+ case "matches":
+ ops = " { fhir:v /";
+ endOps = "/ } ";
+ break;
+ case "where": // 'where' just states an assertion
+ ops = "{ ";
+ endOps = " }";
+ break;
+ case "contains":
+ ops = " { fhir:v [";
+ endOps = "] } ";
+ toQuote = true;
+ break;
+ case "toString": // skip this function call because values gets stringitize anyway
+ pExp = "";
+ break;
+ case "is":
+ ops = " { a [";
+ endOps = "] } ";
+ break;
+ default:
+ fExp = TBD(node.getFunction().toCode());
+ String toStore = addUnmappedFunction(node.getFunction().toCode());
+ }
+
+ if (node.parameterCount() > 0) {
+ for (ExpressionNode pen : node.getParameters()) {
+ if (!"".equals(pExp))
+ pExp += ", ";
+ pExp += processExpressionNode(sd, ed, pen, quote, depth);
+ isFunctionCall = true;
+ }
+ }
+ }
+
+ if (isFunctionCall) {
+ if (!mappedFunctions.contains(funcName))
+ translatedShEx += fExp + "(" + pExp + ")";
+ else {
+ translatedShEx += ops + pExp + endOps;
+ ops = "";
+ endOps = "";
+ }
+ }
+ else
+ translatedShEx += fExp;
+
+ translatedShEx = positionParts(innerShEx, translatedShEx,
+ getNextOps(ops , processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, false);
+
+ } else if (kind == ExpressionNode.Kind.Name) {
+ translatedShEx += positionParts(innerShEx, "fhir:" + node.getName(),
+ getNextOps(ops, processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, true);
+ }else if (kind == ExpressionNode.Kind.Group) {
+ translatedShEx += positionParts(innerShEx, processExpressionNode(sd, ed, node.getGroup(), toQuote, depth),
+ getNextOps(ops , processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, true);
+ } else if (kind == ExpressionNode.Kind.Constant) {
+ Base constantB = node.getConstant();
+ boolean toQt = (constantB instanceof StringType) || (!constantB.isPrimitive());
+ String constantV = constantB.primitiveValue();
+
+ if (constantV.startsWith("%")) {
+ try {
+ // Evaluate the expression, this resolves unknowns in the value.
+ List evaluated = fpe.evaluate(null, sd, sd, ed, node);
+
+ if (!evaluated.isEmpty())
+ constantV = evaluated.get(0).primitiveValue();
+ }
+ catch (Exception e) {
+ debug("Failed to evaluate constant expression: " + constantV);
+ }
+ }
+
+ translatedShEx += positionParts(innerShEx, quoteThis(constantV, toQt),
+ getNextOps(ops , processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, false);
+ //translatedShEx += positionParts(innerShEx, node.getConstant().primitiveValue(), ops + processExpressionNode(node.getOpNext(), toQuote, 0) + endOps, depth);
+ } else if (kind == ExpressionNode.Kind.Unary) {
+ translatedShEx += positionParts(innerShEx, node.getName(),
+ getNextOps(ops,processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, false);
+ } else {
+ translatedShEx += positionParts(innerShEx, node.toString(),
+ getNextOps(ops, processExpressionNode(sd, ed, node.getOpNext(), toQuote, depth), endOps, treatBothOpsSame),
+ depth, false);
+ }
+
+ return translatedShEx;
+ }
+
+ private String addUnmappedFunction(String func) {
+ String toStore = "UNMAPPED_FUNCTION_" + func;
+ if (!unMappedFunctions.contains(toStore))
+ unMappedFunctions.add(toStore);
+
+ return toStore;
+ }
+
+ private String getNextOps(String startOp, String opNext, String endOp, boolean treatBothOps){
+ if (treatBothOps)
+ return startOp + opNext + " " + endOp + opNext;
+
+ return startOp + opNext + endOp;
+ }
+
+ private String positionParts(String funCall, String mainTxt, String nextText, int depth, boolean complete){
+ if (funCall.indexOf("CALLER") != -1) {
+ if (depth == 0) {
+ String toReturn = funCall.replaceFirst("CALLER", mainTxt);
+ if (complete)
+ toReturn = toReturn ;
+
+ toReturn = postProcessing(toReturn, nextText);
+ return toReturn.replaceAll("CALLER", "");
+ }
+ else{
+ return postProcessing(funCall.replaceFirst("CALLER", "CALLER " + mainTxt ), nextText) ;
+ }
+ }
+
+ String fc = funCall;
+ if (fc.startsWith("fhir:"))
+ fc = "." + fc.substring("fhir:".length());
+
+ if ((depth == 0)&&(complete)) {
+ if (mainTxt.startsWith("fhir:")) {
+ if ("".equals(funCall)) {
+ return "{ " + postProcessing(mainTxt, nextText) + " }";
+ }
+ return postProcessing("{ " + mainTxt + fc + " }", nextText);
+ }
+ mainTxt = "(" + mainTxt + ")";
+ if ("".equals(funCall)) {
+ return postProcessing(mainTxt, nextText);
+ }
+ }
+
+ return postProcessing(mainTxt + fc, nextText);
+ }
+
+ private String postProcessing(String p, String q){
+ String qp = q;
+ if ((q != null)&&(q.trim().startsWith("XOR"))){
+ qp = q.split("XOR")[1];
+
+ // because p xor q = ( p and not q) OR (not p and q)
+ return "(" + p + " AND NOT " + qp + ") OR ( NOT " + p + " AND " + qp + ")";
+ }
+
+ return p + qp;
+ }
+
+ /**
+ * @param str
+ * @return
+ */
+ private String TBD(String str){
+ return " SHEX_" + str + "_SHEX ";
+ }
+
+ /**
+ * @param str
+ * @param quote
+ * @return
+ */
+ private String quoteThis(String str, boolean quote){
+
+ if (quote)
+ return "'" + str + "'";
+
+ return str;
+ }
/**
* Generate a flattened definition for the inner types
@@ -398,7 +1028,9 @@ public class ShExGenerator {
StringBuilder itDefs = new StringBuilder();
while(emittedInnerTypes.size() < innerTypes.size()) {
for (Pair it : new HashSet>(innerTypes)) {
- if (!emittedInnerTypes.contains(it)) {
+ if ((!emittedInnerTypes.contains(it))
+ // && (it.getRight().hasBase() && it.getRight().getBase().getPath().startsWith(it.getLeft().getName()))
+ ){
itDefs.append("\n").append(genInnerTypeDef(it.getLeft(), it.getRight()));
emittedInnerTypes.add(it);
}
@@ -407,6 +1039,22 @@ public class ShExGenerator {
return itDefs.toString();
}
+ /**
+ * @param ed
+ * @return
+ */
+ private boolean isInInnerTypes(ElementDefinition ed) {
+
+ if (this.innerTypes.isEmpty())
+ return false;
+
+ for (Iterator> itr = this.innerTypes.iterator(); itr.hasNext(); )
+ if (itr.next().getRight() == ed)
+ return true;
+
+ return false;
+ }
+
/**
* Generate a shape definition for the current set of datatypes
* @return stringified data type definitions
@@ -417,7 +1065,7 @@ public class ShExGenerator {
for (String dt : new HashSet(datatypes)) {
if (!emittedDatatypes.contains(dt)) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class,
- ProfileUtilities.sdNs(dt, null));
+ ProfileUtilities.sdNs(dt, null));
// TODO: Figure out why the line below doesn't work
// if (sd != null && !uniq_structures.contains(sd))
if(sd != null && !uniq_structure_urls.contains(sd.getUrl()))
@@ -429,6 +1077,11 @@ public class ShExGenerator {
return dtDefs.toString();
}
+ /**
+ * @param text
+ * @param max_col
+ * @return
+ */
private ArrayList split_text(String text, int max_col) {
int pos = 0;
ArrayList rval = new ArrayList();
@@ -452,6 +1105,10 @@ public class ShExGenerator {
return rval;
}
+ /**
+ * @param tmplt
+ * @param ed
+ */
private void addComment(ST tmplt, ElementDefinition ed) {
if(withComments && ed.hasShort() && !ed.getId().startsWith("Extension.")) {
int nspaces;
@@ -482,63 +1139,145 @@ public class ShExGenerator {
* @param ed Containing element definition
* @return ShEx definition
*/
- private String genElementDefinition(StructureDefinition sd, ElementDefinition ed) {
+ private String genElementDefinition(StructureDefinition sd,
+ ElementDefinition ed) {
String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath();
- String shortId = id.substring(id.lastIndexOf(".") + 1);
- String defn;
+
+ String shortId = id;
+ String typ = id;
+
+ if (ed.getType().size() > 0)
+ typ = ed.getType().get(0).getCode();
+
+ if (id.equals("Element.extension") && ed.hasSliceName()) {
+ shortId = ed.getSliceName();
+ }
+ else
+ shortId = id.substring(id.lastIndexOf(".") + 1);
+
+ if ((ed.getType().size() > 0) &&
+ (ed.getType().get(0).getCode().startsWith(Constants.NS_SYSTEM_TYPE))) {
+ shortId = "v";
+ typ = ed.getType().get(0).getWorkingCode();
+ }
+
+ String defn = "";
ST element_def;
String card = ("*".equals(ed.getMax()) ? (ed.getMin() == 0 ? "*" : "+") : (ed.getMin() == 0 ? "?" : "")) + ";";
- if(id.endsWith("[x]")) {
- element_def = ed.getType().size() > 1? tmplt(INNER_SHAPE_TEMPLATE) : tmplt(ELEMENT_TEMPLATE);
- element_def.add("id", "");
+ element_def = tmplt(ELEMENT_TEMPLATE);
+ if (id.endsWith("[x]")) {
+ element_def.add("id", "fhir:" + shortId.replace("[x]", ""));
} else {
- element_def = tmplt(ELEMENT_TEMPLATE);
- element_def.add("id", "fhir:" + (id.charAt(0) == id.toLowerCase().charAt(0)? shortId : id) + " ");
+ element_def.add("id", "fhir:" + shortId + " ");
}
List children = profileUtilities.getChildList(sd, ed);
if (children.size() > 0) {
- innerTypes.add(new ImmutablePair(sd, ed));
- defn = simpleElement(sd, ed, id);
- } else if(id.endsWith("[x]")) {
- defn = genChoiceTypes(sd, ed, id);
+ String parentPath = sd.getName();
+ if ((ed.hasContentReference() && (!ed.hasType())) || (!id.equals(parentPath + "." + shortId))) {
+ //debug("Not Adding innerType:" + id + " to " + sd.getName());
+ } else
+ innerTypes.add(new ImmutablePair(sd, ed));
}
- else if (ed.getType().size() == 1) {
- // Single entry
- defn = genTypeRef(sd, ed, id, ed.getType().get(0));
- } else if (ed.getContentReference() != null) {
- // Reference to another element
- String ref = ed.getContentReference();
- if(!ref.startsWith("#"))
- throw new AssertionError("Not equipped to deal with absolute path references: " + ref);
- String refPath = null;
- for(ElementDefinition ed1: sd.getSnapshot().getElement()) {
- if(ed1.getId() != null && ed1.getId().equals(ref.substring(1))) {
- refPath = ed1.getPath();
- break;
+
+ defn = simpleElement(sd, ed, typ);
+
+ String refChoices = "";
+
+ if (id.endsWith("[x]")) {
+ defn = " (" + genChoiceTypes(sd, ed, shortId) + ") ";
+ defn += " AND { rdf:type IRI } ";
+ } else {
+ if (ed.getType().size() == 1) {
+ // Single entry
+ if ((defn.isEmpty())||(typ.equals(sd.getName())))
+ defn = genTypeRef(sd, ed, id, ed.getType().get(0));
+ } else if (ed.getContentReference() != null) {
+ // Reference to another element
+ String ref = ed.getContentReference();
+ if (!ref.startsWith("#"))
+ throw new AssertionError("Not equipped to deal with absolute path references: " + ref);
+ String refPath = null;
+ for (ElementDefinition ed1 : sd.getSnapshot().getElement()) {
+ if (ed1.getId() != null && ed1.getId().equals(ref.substring(1))) {
+ refPath = ed1.getPath();
+ break;
+ }
+ }
+ if (refPath == null)
+ throw new AssertionError("Reference path not found: " + ref);
+
+ defn = simpleElement(sd, ed, refPath);
+ }
+
+
+ List refValues = new ArrayList();
+ if (ed.hasType() && (ed.getType().get(0).getWorkingCode().equals("Reference"))) {
+ if (ed.getType().get(0).hasTargetProfile()) {
+
+ ed.getType().get(0).getTargetProfile().forEach((CanonicalType tps) -> {
+ String els[] = tps.getValue().split("/");
+ refValues.add(els[els.length - 1]);
+ });
}
}
- if(refPath == null)
- throw new AssertionError("Reference path not found: " + ref);
- // String typ = id.substring(0, id.indexOf(".") + 1) + ed.getContentReference().substring(1);
- defn = simpleElement(sd, ed, refPath);
- } else if(id.endsWith("[x]")) {
- defn = genChoiceTypes(sd, ed, id);
- } else {
- // TODO: Refactoring required here
- element_def = genAlternativeTypes(ed, id, shortId);
- element_def.add("id", id.charAt(0) == id.toLowerCase().charAt(0)? shortId : id);
- element_def.add("card", card);
- addComment(element_def, ed);
- return element_def.render();
+
+ if (!refValues.isEmpty()) {
+ Collections.sort(refValues);
+ refChoices = StringUtils.join(refValues, "_OR_");
+ }
}
+
+ // Adding OneOrMore as prefix to the reference type if cardinality is 1..* or 0..*
+ if (card.startsWith("*") || card.startsWith("+")) {
+ card = card.replace("+", "");
+ card = card.replace("*", "?");
+ defn = defn.replace("<", "<" + ONE_OR_MORE_PREFIX);
+
+ String defnToStore = defn;
+ if (!refChoices.isEmpty()) {
+ defnToStore = defn.replace(">", ONE_OR_MORE_CHOICES + refChoices + ">");
+ defn = defn.replace(">", "_" + refChoices + ">");
+ }
+
+ defnToStore = StringUtils.substringBetween(defnToStore, "<", ">");
+ if (!oneOrMoreTypes.contains(defnToStore))
+ oneOrMoreTypes.add(defnToStore);
+ } else {
+ if (!refChoices.isEmpty()) {
+ defn += " AND {fhir:link \n\t\t\t@<" +
+ refChoices.replaceAll("_OR_", "> OR \n\t\t\t@<") + "> }";
+ }
+ }
+
element_def.add("defn", defn);
element_def.add("card", card);
addComment(element_def, ed);
+
return element_def.render();
}
+ private List getChildren(StructureDefinition derived, ElementDefinition element) {
+ List elements = derived.getSnapshot().getElement();
+ int index = elements.indexOf(element) + 1;
+ String path = element.getPath()+".";
+ List list = new ArrayList<>();
+ while (index < elements.size()) {
+ ElementDefinition e = elements.get(index);
+ String p = e.getPath();
+ if (p.startsWith(path) && !e.hasSliceName()) {
+ if (!p.substring(path.length()).contains(".")) {
+ list.add(e);
+ }
+ index++;
+ } else {
+ break;
+ }
+ }
+ return list;
+ }
+
/**
* Generate a type reference and optional value set definition
* @param sd Containing StructureDefinition
@@ -595,7 +1334,7 @@ public class ShExGenerator {
} else if (typ.getCode().startsWith(Constants.NS_SYSTEM_TYPE)) {
String xt = getShexCode(typ.getWorkingCode());
-
+
// TODO: Remove the next line when the type of token gets switched to string
// TODO: Add a rdf-type entry for valueInteger to xsd:integer (instead of int)
ST td_entry = tmplt(PRIMITIVE_ELEMENT_DEFN_TEMPLATE).add("typ", xt);
@@ -632,51 +1371,55 @@ public class ShExGenerator {
}
}
+ /**
+ * @param c
+ * @return
+ */
private String getShexCode(String c) {
switch (c) {
- case "boolean" :
- return "xsd:boolean";
- case "integer" :
- return "xsd:int";
- case "integer64" :
- return "xsd:long";
- case "decimal" :
- return "xsd:decimal, xsd:double";
- case "base64Binary" :
- return "xsd:base64Binary";
- case "instant" :
- return "xsd:dateTime";
- case "string" :
- return "xsd:string";
- case "uri" :
- return "xsd:anyURI";
- case "date" :
- return "xsd:gYear, xsd:gYearMonth, xsd:date";
- case "dateTime" :
- return "xsd:gYear, xsd:gYearMonth, xsd:date, xsd:dateTime";
- case "time" :
- return "xsd:time";
- case "code" :
- return "xsd:token";
- case "oid" :
- return "xsd:anyURI";
- case "uuid" :
- return "xsd:anyURI";
- case "url" :
- return "xsd:anyURI";
- case "canonical" :
- return "xsd:anyURI";
- case "id" :
- return "xsd:string";
- case "unsignedInt" :
- return "xsd:nonNegativeInteger";
- case "positiveInt" :
- return "xsd:positiveInteger";
- case "markdown" :
- return "xsd:string";
+ case "boolean" :
+ return "xsd:boolean";
+ case "integer" :
+ return "xsd:int";
+ case "integer64" :
+ return "xsd:long";
+ case "decimal" :
+ return "xsd:decimal OR xsd:double";
+ case "base64Binary" :
+ return "xsd:base64Binary";
+ case "instant" :
+ return "xsd:dateTime";
+ case "string" :
+ return "xsd:string";
+ case "uri" :
+ return "xsd:anyURI";
+ case "date" :
+ return "xsd:gYear OR xsd:gYearMonth OR xsd:date";
+ case "dateTime" :
+ return "xsd:gYear OR xsd:gYearMonth OR xsd:date OR xsd:dateTime";
+ case "time" :
+ return "xsd:time";
+ case "code" :
+ return "xsd:token";
+ case "oid" :
+ return "xsd:anyURI";
+ case "uuid" :
+ return "xsd:anyURI";
+ case "url" :
+ return "xsd:anyURI";
+ case "canonical" :
+ return "xsd:anyURI";
+ case "id" :
+ return "xsd:string";
+ case "unsignedInt" :
+ return "xsd:nonNegativeInteger";
+ case "positiveInt" :
+ return "xsd:positiveInteger";
+ case "markdown" :
+ return "xsd:string";
}
throw new Error("Not implemented yet");
-
+
}
/**
@@ -694,7 +1437,7 @@ public class ShExGenerator {
for(ElementDefinition.TypeRefComponent typ : ed.getType()) {
altEntries.add(genAltEntry(id, typ));
}
- shex_alt.add("altEntries", StringUtils.join(altEntries, " OR\n "));
+ shex_alt.add("altEntries", StringUtils.join(altEntries, " OR \n "));
return shex_alt;
}
@@ -720,14 +1463,29 @@ public class ShExGenerator {
* @param id choice identifier
* @return ShEx fragment for the set of choices
*/
- private String genChoiceTypes(StructureDefinition sd, ElementDefinition ed, String id) {
+ private String genChoiceTypes(StructureDefinition sd,
+ ElementDefinition ed,
+ String id) {
List choiceEntries = new ArrayList();
+ List refValues = new ArrayList();
String base = id.replace("[x]", "");
- for(ElementDefinition.TypeRefComponent typ : ed.getType())
- choiceEntries.add(genChoiceEntry(sd, ed, id, base, typ));
+ for (ElementDefinition.TypeRefComponent typ : ed.getType()) {
+ String entry = genChoiceEntry(sd, ed, "", "", typ);
+ refValues.clear();
+ if (typ.hasTargetProfile()) {
+ typ.getTargetProfile().forEach((CanonicalType tps) -> {
+ String els[] = tps.getValue().split("/");
+ refValues.add("@<" + els[els.length - 1] + ">");
+ });
+ }
- return StringUtils.join(choiceEntries, " |\n");
+ if (!refValues.isEmpty())
+ choiceEntries.add("(" + entry + " AND {fhir:link " + StringUtils.join(refValues, " OR \n\t\t\t ") + " }) ");
+ else
+ choiceEntries.add(entry);
+ }
+ return StringUtils.join(choiceEntries, " OR \n\t\t\t");
}
/**
@@ -736,17 +1494,52 @@ public class ShExGenerator {
* @param typ type/discriminant
* @return ShEx fragment for choice entry
*/
- private String genChoiceEntry(StructureDefinition sd, ElementDefinition ed, String id, String base, ElementDefinition.TypeRefComponent typ) {
+ private String genChoiceEntry(StructureDefinition sd,
+ ElementDefinition ed,
+ String id,
+ String base,
+ ElementDefinition.TypeRefComponent typ) {
ST shex_choice_entry = tmplt(ELEMENT_TEMPLATE);
String ext = typ.getWorkingCode();
- shex_choice_entry.add("id", "fhir:" + base+Character.toUpperCase(ext.charAt(0)) + ext.substring(1) + " ");
+ // shex_choice_entry.add("id", "fhir:" + base+Character.toUpperCase(ext.charAt(0)) + ext.substring(1) + " ");
+ shex_choice_entry.add("id", "");
shex_choice_entry.add("card", "");
shex_choice_entry.add("defn", genTypeRef(sd, ed, id, typ));
shex_choice_entry.add("comment", " ");
return shex_choice_entry.render();
}
+ /**
+ * @param oneOrMoreType
+ * @return
+ */
+ private String getOneOrMoreType(String oneOrMoreType) {
+ if ((oneOrMoreType == null)||(oneOrMoreTypes.isEmpty()))
+ return "";
+
+ ST one_or_more_type = tmplt(ONE_OR_MORE_TEMPLATE);
+ String oomType = oneOrMoreType;
+ String origType = oneOrMoreType;
+ String restriction = "";
+ if (oneOrMoreType.indexOf(ONE_OR_MORE_CHOICES) != -1) {
+ oomType = oneOrMoreType.replaceAll(ONE_OR_MORE_CHOICES, "_");
+ origType = oneOrMoreType.split(ONE_OR_MORE_CHOICES)[0];
+ restriction = "AND {fhir:link \n\t\t\t@<";
+
+ String choices = oneOrMoreType.split(ONE_OR_MORE_CHOICES)[1];
+ restriction += choices.replaceAll("_OR_", "> OR \n\t\t\t@<") + "> }";
+ }
+
+ origType = origType.replaceAll(ONE_OR_MORE_PREFIX, "");
+
+ one_or_more_type.add("oomType", oomType);
+ one_or_more_type.add("origType", origType);
+ one_or_more_type.add("restriction", restriction);
+ one_or_more_type.add("comment", "");
+ return one_or_more_type.render();
+ }
+
/**
* Generate a definition for a referenced element
* @param sd Containing structure definition
@@ -754,18 +1547,59 @@ public class ShExGenerator {
* @return ShEx representation of element reference
*/
private String genInnerTypeDef(StructureDefinition sd, ElementDefinition ed) {
- String path = ed.hasBase() ? ed.getBase().getPath() : ed.getPath();;
+ String path = ed.hasBase() ? ed.getBase().getPath() : ed.getPath();
ST element_reference = tmplt(SHAPE_DEFINITION_TEMPLATE);
element_reference.add("resourceDecl", ""); // Not a resource
- element_reference.add("id", path);
+ element_reference.add("id", path + getExtendedType(ed));
+ element_reference.add("fhirType", " ");
String comment = ed.getShort();
element_reference.add("comment", comment == null? " " : "# " + comment);
List elements = new ArrayList();
for (ElementDefinition child: profileUtilities.getChildList(sd, path, null))
- elements.add(genElementDefinition(sd, child));
+ if (child.hasBase() && child.getBase().getPath().startsWith(sd.getName())) {
+ String elementDefinition = genElementDefinition(sd, child);
+ elements.add(elementDefinition);
+ }
element_reference.add("elements", StringUtils.join(elements, "\n"));
+
+ List innerConstraintsList = new ArrayList();
+ // Process constraints
+ for (ElementDefinition.ElementDefinitionConstraintComponent constraint : ed.getConstraint()) {
+ String sdType = sd.getType();
+ String cstype = constraint.getSource();
+ if ((!cstype.isEmpty()) && (cstype.indexOf("/") != -1)) {
+ String[] els = cstype.split("/");
+ cstype = els[els.length - 1];
+ }
+
+ String id = ed.hasBase() ? ed.getBase().getPath() : ed.getPath();
+ String shortId = id.substring(id.lastIndexOf(".") + 1);
+ if ((ed.hasContentReference() && (!ed.hasType())) || (id.equals(sd.getName() + "." + shortId))) {
+ if ((sdType.equals(cstype)) || baseDataTypes.contains(sdType)) {
+ //if (!isInInnerTypes(ed)) {
+ debug("\n (INNER ED) Key: " + constraint.getKey() + " SD type: " + sd.getType() + " Element: " + ed.getPath() + " Constraint Source: " + constraint.getSource() + " Constraint:" + constraint.getExpression());
+ String transl = translateConstraint(sd, ed, constraint);
+ if (transl.isEmpty() || innerConstraintsList.contains(transl))
+ continue;
+ innerConstraintsList.add(transl);
+ //}
+ }
+ }
+ }
+
+ String constraintStr = "";
+
+ if (!innerConstraintsList.isEmpty()) {
+ constraintStr = "AND (\n\n" + StringUtils.join(constraintsList, "\n\n) AND (\n\n") + "\n\n)\n";
+ }
+
+ element_reference.add("constraints", constraintStr);
+
+ // TODO: See if we need to process contexts
+ element_reference.add("contextOfUse", "");
+
return element_reference.render();
}
@@ -803,14 +1637,18 @@ public class ShExGenerator {
}
}
+ /**
+ * @param vs
+ * @return
+ */
private String genValueSet(ValueSet vs) {
ST vsd = tmplt(VALUE_SET_DEFINITION).add("vsuri", vsprefix(vs.getUrl())).add("comment", vs.getDescription());
ValueSetExpander.ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
List valid_codes = new ArrayList();
if(vse != null &&
- vse.getValueset() != null &&
- vse.getValueset().hasExpansion() &&
- vse.getValueset().getExpansion().hasContains()) {
+ vse.getValueset() != null &&
+ vse.getValueset().hasExpansion() &&
+ vse.getValueset().getExpansion().hasContains()) {
for(ValueSet.ValueSetExpansionContainsComponent vsec : vse.getValueset().getExpansion().getContains())
valid_codes.add("\"" + vsec.getCode() + "\"");
}
@@ -818,6 +1656,11 @@ public class ShExGenerator {
}
+ /**
+ * @param ctxt
+ * @param reference
+ * @return
+ */
// TODO: find a utility that implements this
private ValueSet resolveBindingReference(DomainResource ctxt, String reference) {
try {
@@ -826,4 +1669,13 @@ public class ShExGenerator {
return null;
}
}
+
+ private void debug(String message) {
+ if (this.debugMode)
+ System.out.println(message);
+ }
+
+ private void printBuildMessage(String message){
+ System.out.println("ShExGenerator: " + message);
+ }
}
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java
new file mode 100644
index 000000000..7e210bc1f
--- /dev/null
+++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTestUtils.java
@@ -0,0 +1,168 @@
+package org.hl7.fhir.r5.test;
+
+import org.hl7.fhir.r5.model.StructureDefinition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ShexGeneratorTestUtils {
+ public class resDef {
+ public String name;
+ public String url;
+ public String info;
+
+ public resDef(String _name, String _url, String _info){
+ this.name = _name;
+ this.url = _url;
+ this.info = _info;
+ }
+
+ @Override
+ public String toString() {
+ return " " + name + "[ " + url + " ] ";
+ }
+ }
+
+ public enum RESOURCE_CATEGORY{
+ LOGICAL_NAMES, STRUCTURE_DEFINITIONS, EXTENSIONS, PROFILES, ALL
+ }
+ public List getSDs(List sds, RESOURCE_CATEGORY cat) {
+ List selSDs = new ArrayList();
+ sds.forEach((StructureDefinition sd) -> {
+ switch(cat) {
+ case STRUCTURE_DEFINITIONS:
+ if (sd.getType().trim().equals(sd.getName().trim()))
+ selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
+ break;
+ case LOGICAL_NAMES:
+ if (sd.getBaseDefinition() == null)
+ selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
+ break;
+ case EXTENSIONS:
+ if ("Extension".equals(sd.getType()))
+ selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
+ break;
+ case PROFILES:
+ if (!((sd.getBaseDefinition() == null) ||
+ ("Extension".equals(sd.getType())) ||
+ (sd.getType().trim().equals(sd.getName().trim()))))
+ selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
+ break;
+ default:
+ selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
+ }
+ });
+
+ Collections.sort(selSDs, new Comparator() {
+ @Override
+ public int compare(resDef o1, resDef o2) {
+ return o1.name.compareTo(o2.name);
+ }
+ });
+
+ return selSDs;
+ }
+ public static List getMetaStructureDefinitionsToSkip(){
+ List skipSDs = new ArrayList();
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ActivityDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/AdministrableProductDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ClinicalUseDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/CodeSystem");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ConceptMap");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/CompartmentDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/CompartmentDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/DeviceDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ElementDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/EventDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/GraphDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/GuidanceResponse");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/Library");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ManufacturedItemDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/Measure");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/MeasureReport");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/MedicinalProductDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/MessageDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/NamingSystem");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ObservationDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/OperationDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/PackagedProductDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ParameterDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/PlanDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/SearchParameter");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/SpecimenDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/StructureDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/SubstanceDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/Task");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/TerminologyCapabilities");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/TriggerDefinition");
+ skipSDs.add("http://hl7.org/fhir/StructureDefinition/ValueSet");
+ return skipSDs;
+ }
+
+ /**
+ * This method prepares selected extensions to test the policy in which ShEx Generator
+ * only translates selected resource extensions
+ * @return List List of selected resource extensions for testing
+ */
+ public static List getSelectedExtensions(){
+ List selectedExtesnsions = new ArrayList();
+ selectedExtesnsions.add("http://fhir-registry.smarthealthit.org/StructureDefinition/capabilities");
+ selectedExtesnsions.add("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
+ return selectedExtesnsions;
+ }
+
+ public static String getSDInfo(StructureDefinition sd) {
+ if (sd != null) {
+ String kind = " ";
+ try {
+ kind = sd.getKind().name();
+ } catch (Exception e) {
+ System.out.println("Kind is null");
+ }
+ String name = " ";
+ try {
+ name = sd.getName();
+ } catch (Exception e) {
+ System.out.println("Name is null");
+ }
+ String type = " ";
+ try {
+ type = sd.getType();
+ } catch (Exception e) {
+ System.out.println("Type is null");
+ }
+ String derv = " ";
+ try {
+ derv = sd.getDerivation().name();
+ } catch (Exception e) {
+ System.out.println("Derivation is null");
+ }
+ String url = " ";
+ try {
+ url = sd.getUrl();
+ } catch (Exception e) {
+ System.out.println("URL is null");
+ }
+ String base = " ";
+ try {
+ base = sd.getBaseDefinition();
+ } catch (Exception e) {
+ System.out.println("Base is null");
+ }
+ return kind + "\t" + name + "\t" + type + "\t" + derv + "\t" + url + "\t" + base;
+ }
+ return "";
+ }
+
+ public static void printList(String title, List items) {
+ System.out.println("************************************************************************");
+ System.out.println("Printing " + title);
+ System.out.println("************************************************************************");
+ items.forEach((resDef item) -> {
+ System.out.println(item.name + " [" + item.url + "]");
+ });
+ }
+}
diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java
index 78cf1b8ca..3c66045b6 100644
--- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java
+++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ShexGeneratorTests.java
@@ -4,18 +4,37 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import es.weso.shex.Schema;
+import es.weso.shex.validator.ShExsValidator;
+import es.weso.shex.validator.ShExsValidatorBuilder;
+import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.UcumException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.conformance.ShExGenerator;
import org.hl7.fhir.r5.conformance.ShExGenerator.HTMLLinkPolicy;
+import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TextFile;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import static org.hl7.fhir.r5.test.ShexGeneratorTestUtils.printList;
+
public class ShexGeneratorTests {
+ public static List selectedExtesnsions = new ArrayList();
+
+ public ShExGenerator shexGenerator;
+
+ @BeforeAll
+ public static void setup() {
+ }
private void doTest(String name) throws FileNotFoundException, IOException, FHIRException, UcumException {
StructureDefinition sd = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
@@ -24,8 +43,11 @@ public class ShexGeneratorTests {
}
Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex");
TextFile.stringToFile(new ShExGenerator(TestingUtilities.getSharedWorkerContext()).generate(HTMLLinkPolicy.NONE, sd), outPath.toString());
- }
+ // For Testing Schema Processing and Constraint Mapping related Development
+ // If you un-comment the following lines, please comment all other lines in this method.
+ //this.doTestThis(name.toLowerCase(), name, false, ShExGenerator.ConstraintTranslationPolicy.ALL, false, true);
+ }
@Test
public void testId() throws FHIRException, IOException, UcumException {
doTest("id");
@@ -75,4 +97,193 @@ public class ShexGeneratorTests {
public void testSignature() throws FHIRException, IOException, UcumException {
doTest("Signature");
}
+
+ @Test
+ public void testString() throws FHIRException, IOException, UcumException {
+ doTest("string");
+ }
+
+ private void doTestThis(String shortName, String name, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx) {
+ IWorkerContext ctx = TestingUtilities.getSharedWorkerContext();
+ StructureDefinition sd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
+ if (sd == null) {
+ throw new FHIRException("StructuredDefinition for " + name + " was null");
+ }
+ //Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex");
+ Path outPath = FileSystems.getDefault().getPath(System.getProperty("user.home") + "/runtime_environments/ShExSchemas", shortName + ".shex");
+ try {
+ this.shexGenerator = new ShExGenerator(ctx);
+
+ this.shexGenerator.debugMode = debugMode;
+ this.shexGenerator.constraintPolicy = policy;
+
+ // ShEx Generator skips resources which are at Meta level of FHIR Resource definitions
+ this.shexGenerator.setExcludedStructureDefinitionUrls(
+ ShexGeneratorTestUtils.getMetaStructureDefinitionsToSkip());
+
+ // when ShEx translates only selected resource extensions
+ if (useSelectedExtensions) {
+ List selExtns = new ArrayList();
+ for (String eUrl : ShexGeneratorTestUtils.getSelectedExtensions()) {
+ StructureDefinition esd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(eUrl, null));
+ if (esd != null)
+ selExtns.add(esd);
+ }
+ this.shexGenerator.setSelectedExtension(selExtns);
+ }
+
+ String schema = this.shexGenerator.generate(HTMLLinkPolicy.NONE, sd);
+ if (!schema.isEmpty()) {
+
+ if (validateShEx) {
+ try {
+ ShExsValidator validator = ShExsValidatorBuilder.fromStringSync(schema, "ShexC");
+ Schema sch = validator.schema();
+
+ Assert.assertNotNull(sch);
+ System.out.println("VALIDATION PASSED for ShEx Schema " + sd.getName());
+ } catch (Exception e) {
+ System.out.println("VALIDATION FAILED for ShEx Schema " + sd.getName());
+ //System.out.println("\t\t\tMessage: " + e.getMessage());
+ }
+ }
+ TextFile.stringToFile(schema, outPath.toString());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Ignore
+ public void doTestAll() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process all extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL
+ // Process all types of constraints, do not skip
+ );
+ }
+
+ @Ignore
+ public void doTestGenericExtensionsOnlyPolicy() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process all extensions
+ ShExGenerator.ConstraintTranslationPolicy.GENERIC_ONLY
+ // Process generic constraints only, ignore constraints of type 'context of use'
+ );
+
+ }
+
+ @Ignore
+ public void doTestContextOfUseExtensionsOnlyPolicy() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process all extensions
+ ShExGenerator.ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY
+ // Process constraints only where context of use found, skip otherwise
+ );
+ }
+
+ @Ignore
+ public void doTestSelectedExtensions() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ true, //Process only given/selected extensions, ignore other extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
+ );
+ }
+
+ @Ignore
+ public void testStructureDefinitionsOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITIONS, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process only given/selected extensions, ignore other extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
+ );
+ }
+
+ @Ignore
+ public void testExtensionsOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.EXTENSIONS, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process only given/selected extensions, ignore other extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
+ );
+ }
+
+ @Ignore
+ public void testLogicalNamesOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.LOGICAL_NAMES, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process only given/selected extensions, ignore other extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
+ );
+ }
+
+ @Ignore
+ public void testProfilesOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
+ List sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
+ processSDList(
+ ShexGeneratorTestUtils.RESOURCE_CATEGORY.PROFILES, // Processing All kinds of Structure Definitions
+ sds, // List of Structure Definitions
+ false, //Process only given/selected extensions, ignore other extensions
+ ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
+ );
+ }
+
+ private void processSDList(ShexGeneratorTestUtils.RESOURCE_CATEGORY cat,
+ List sds,
+ boolean useSelectedExtensions,
+ ShExGenerator.ConstraintTranslationPolicy policy) {
+ if ((sds == null) || (sds.isEmpty())) {
+ throw new FHIRException("No StructuredDefinition found!");
+ }
+
+ ShexGeneratorTestUtils shexTestUtils = new ShexGeneratorTestUtils();
+ List sdDefs = shexTestUtils.getSDs(sds, cat);
+
+ printList(cat.toString(), sdDefs);
+ System.out.println("************************************************************************");
+ System.out.println("Processing " + cat);
+ System.out.println("************************************************************************");
+
+ sdDefs.forEach((ShexGeneratorTestUtils.resDef resDef) -> {
+ String name = resDef.url;
+ if (resDef.url.indexOf("/") != -1) {
+ String els[] = resDef.url.split("/");
+ name = els[els.length - 1];
+ }
+ System.out.println("******************** " + resDef + " *********************");
+ doTestThis(name, resDef.url, useSelectedExtensions, policy, true, true);
+ });
+
+ System.out.println("************************ END PROCESSING ******************************");
+
+ System.out.println("************************************************************************");
+ List skipped = this.shexGenerator.getExcludedStructureDefinitionUrls();
+ System.out.println("Total Items processed: " + sds.size());
+ System.out.println("************************************************************************");
+ }
}
\ No newline at end of file