Merge pull request #1850 from hapifhir/2024-12-gg-many-fixes

2024 12 gg many fixes
This commit is contained in:
Grahame Grieve 2024-12-08 19:59:24 +03:00 committed by GitHub
commit 17dfc5c285
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 990 additions and 132 deletions

View File

@ -1,5 +1,8 @@
## Validator Changes
* Add support for valueset-version
* Add support for terminology extraction
* Add support for expansion parameters when validating
* fix NPE in validator around Extension context
* Handle secondary terminology server errors properly
* Fix questionnaire response status checking
@ -7,6 +10,12 @@
* hide API-Key from appearing on the tx log
* Add supplements for used systems as well as for value set systems when validating on server
* fix missing port from server when doing tx-registry redirections
* Fix problem not finding current version of extensions pack for non-R5 versions
* Fix validation of displays when language is unknown
* fix issue missing idrefs validating IPS documents
* Update FHIRPath validation to handle rootResource type properly
* Fix obscure error on contentReference in profiles in FHIRPath engine
* Fix version conversion issue for validating derived questionnaires
## Other code changes
@ -25,3 +34,9 @@
* Fix version issues in snapshot generation tests
* Eliminate id from snapshot generation test case comparison
* Change rules around stripping extensions when generating snapshots
* fix bug using wrong reference on uri in liquid renderer
* add translations for expansion errors
* fix issue with comparison template missing
* Apply null pointer check to all switch(Enumeration) statements in version conversion code
* Remove mysql dependency
* Fix bug in DecimalType on null Bigdecimal ()] all versions)

View File

@ -1,6 +1,7 @@
package org.hl7.fhir.convertors;
import org.hl7.fhir.r5.renderers.QuestionnaireRenderer;
import org.hl7.fhir.r5.utils.ToolingExtensions;
/*
Copyright (c) 2011+, HL7, Inc.
@ -43,7 +44,7 @@ public class VersionConvertorConstants {
public final static String IG_CONFORMANCE_MESSAGE_EVENT = "http://hl7.org/fhir/1.0/StructureDefinition/extension-Conformance.messaging.event";
public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence";
public static final String EXT_ACTUAL_RESOURCE_NAME = "http://hl7.org/fhir/tools/StructureDefinition/original-resource-name";
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = QuestionnaireRenderer.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL;
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL;
public static final String EXT_NAMINGSYSTEM_TITLE = "http://hl7.org/fhir/5.0/StructureDefinition/extension-NamingSystem.title";
public static final String EXT_NAMINGSYSTEM_URL = "http://hl7.org/fhir/5.0/StructureDefinition/extension-NamingSystem.url";
public static final String EXT_NAMINGSYSTEM_VERSION = "http://hl7.org/fhir/5.0/StructureDefinition/extension-NamingSystem.version";

View File

@ -187,6 +187,13 @@ public class FHIRPathEngine {
// the application can implement them by providing a constant resolver
public interface IEvaluationContext {
public abstract class FunctionDefinition {
public abstract String name();
public abstract FunctionDetails details();
public abstract TypeDetails check(FHIRPathEngine engine, Object appContext, TypeDetails focus, List<TypeDetails> parameters);
public abstract List<Base> execute(FHIRPathEngine engine, Object appContext, List<Base> focus, List<List<Base>> parameters);
}
/**
* A constant reference - e.g. a reference to a name that must be resolved in context.
* The % will be removed from the constant name before this is invoked.
@ -476,8 +483,8 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, resourceType, context, expr, null);
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, rootResourceType, resourceType, context, expr, null);
}
/**
@ -492,7 +499,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, String context, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types;
@ -533,7 +540,7 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
}
/**
@ -548,7 +555,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails checkOnTypes(Object appContext, String resourceType, List<String> typeList, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, List<String> typeList, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
// if context is a path that refers to a type, do that conversion now
@ -598,14 +605,21 @@ public class FHIRPathEngine {
}
}
}
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, false, expr);
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, false, expr);
warnings.addAll(typeWarnings);
return res;
}
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, false, expr);
warnings.addAll(typeWarnings);
return res;
}
public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings, boolean canBeNone) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, false, expr);
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, canBeNone, expr);
warnings.addAll(typeWarnings);
return res;
}
@ -621,7 +635,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, List<String> resourceTypes, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, List<String> resourceTypes, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types = null;
@ -633,7 +647,7 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
}
private FHIRException makeExceptionPlural(Integer num, ExpressionNode holder, String constName, Object... args) {
@ -660,7 +674,7 @@ public class FHIRPathEngine {
}
}
public TypeDetails check(Object appContext, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types;
if (!context.contains(".")) {
@ -686,17 +700,17 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, sd.getUrl(), types, types), types, expr, null, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, sd.getUrl(), types, types), types, expr, null, true, false, expr);
}
public TypeDetails check(Object appContext, StructureDefinition sd, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, StructureDefinition sd, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types = null; // this is a special case; the first path reference will have to resolve to something in the context
return executeType(new ExecutionTypeContext(appContext, sd == null ? null : sd.getUrl(), null, types), types, expr, null, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, sd == null ? null : sd.getUrl(), null, types), types, expr, null, true, false, expr);
}
public TypeDetails check(Object appContext, String resourceType, String context, String expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, resourceType, context, parse(expr));
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, String expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, rootResourceType, resourceType, context, parse(expr));
}
private Integer compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
@ -1093,10 +1107,12 @@ public class FHIRPathEngine {
private TypeDetails thisItem;
private TypeDetails total;
private Map<String, TypeDetails> definedVariables;
private String rootResource;
public ExecutionTypeContext(Object appInfo, String resource, TypeDetails context, TypeDetails thisItem) {
public ExecutionTypeContext(Object appInfo, String rootResource, String resource, TypeDetails context, TypeDetails thisItem) {
super();
this.appInfo = appInfo;
this.rootResource = rootResource;
this.resource = resource;
this.context = context;
this.thisItem = thisItem;
@ -3208,10 +3224,10 @@ public class FHIRPathEngine {
}
return new TypeDetails(CollectionStatus.SINGLETON, context.resource);
} else if (s.equals("%rootResource")) {
if (context.resource == null) {
if (context.rootResource == null) {
throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus rootResource");
}
return new TypeDetails(CollectionStatus.SINGLETON, context.resource);
return new TypeDetails(CollectionStatus.SINGLETON, context.rootResource);
} else if (s.equals("%context")) {
return context.context;
} else if (s.equals("%map-codes")) {
@ -3847,6 +3863,18 @@ public class FHIRPathEngine {
}
private boolean typeCastIsImpossible(TypeDetails focus, String tn) {
for (ProfiledType pt : focus.getProfiledTypes()) {
if (!pt.hasProfiles()) { // get back to this later
String purl = tn;
while (purl != null && !purl.equals(pt.getUri())) {
StructureDefinition sd = worker.fetchResource(StructureDefinition.class, purl);
purl = sd == null ? null : sd.getBaseDefinition();
}
if (purl != null) {
return false;
}
}
}
return !focus.hasType(tn);
}
@ -4790,7 +4818,7 @@ public class FHIRPathEngine {
}
private ExecutionTypeContext changeThis(ExecutionTypeContext context, TypeDetails newThis) {
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, newThis);
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.rootResource, context.resource, context.context, newThis);
// append all of the defined variables from the context into the new context
if (context.definedVariables != null) {
for (String s : context.definedVariables.keySet()) {
@ -4801,7 +4829,7 @@ public class FHIRPathEngine {
}
private ExecutionTypeContext contextForParameter(ExecutionTypeContext context) {
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, context.thisItem);
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.rootResource, context.resource, context.context, context.thisItem);
// append all of the defined variables from the context into the new context
if (context.definedVariables != null) {
for (String s : context.definedVariables.keySet()) {
@ -6298,6 +6326,12 @@ public class FHIRPathEngine {
if (ed.getPath().startsWith(path)) {
if (ed.hasContentReference()) {
String cpath = ed.getContentReference();
if (!cpath.startsWith("#")) {
if (!cpath.contains("#")) {
throw new Error("ContentReference doesn't contain a #: "+cpath);
}
cpath = cpath.substring(cpath.indexOf("#"));
}
String tn = sdi.getType()+cpath;
if (!result.hasType(worker, tn)) {
if (elementDependencies != null) {
@ -6513,15 +6547,15 @@ public class FHIRPathEngine {
}
if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) {
ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference());
List<ElementDefinitionMatch> res = getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr);
if (res.size() == 0) {
throw new Error("Unable to find "+ed.getContentReference());
if (m == null) {
throw new Error("Unable to find path "+path+" with a content reference of "+ed.getContentReference());
} else {
List<ElementDefinitionMatch> res = getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr);
for (ElementDefinitionMatch item : res) {
item.sourceDefinition = ed;
}
return res;
}
return res;
}
}
return ml(null);
@ -6563,8 +6597,13 @@ public class FHIRPathEngine {
}
private ElementDefinitionMatch getElementDefinitionById(StructureDefinition sd, String ref) {
if (ref.startsWith(sd.getUrl()+"#")) {
ref = ref.replace(sd.getUrl()+"#", "#");
StructureDefinition sdt = sd;
while (sdt != null) {
if (ref.startsWith(sdt.getUrl()+"#")) {
ref = ref.replace(sdt.getUrl()+"#", "#");
break;
}
sdt = worker.fetchResource(StructureDefinition.class, sdt.getBaseDefinition());
}
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ref.equals("#"+ed.getId())) {

View File

@ -2904,7 +2904,7 @@ public class StructureMapUtilities {
expr = fpe.parse(getParamString(vars, tgt.getParameter().get(tgt.getParameter().size() - 1)));
tgt.setUserData(MAP_WHERE_EXPRESSION, expr);
}
return fpe.check(vars, null, expr);
return fpe.check(vars, "Resource", null, expr);
////case TRUNCATE :
//// String src = getParamString(vars, tgt.getParameter().get(0));

View File

@ -289,7 +289,7 @@ public class Validator {
try {
node = fpe.parse(expr);
column.setUserData("path", node);
td = fpe.checkOnTypes(vd, resourceName, t, node, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, node, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -471,7 +471,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
focus.setUserData("forEach", n);
td = fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -496,7 +496,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
focus.setUserData("forEachOrNull", n);
td = fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -607,7 +607,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
where.setUserData("path", n);
td = fpe.checkOnTypes(vd, resourceName, types, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, types, n, warnings);
} catch (Exception e) {
error(path, where.get("path"), e.getMessage(), IssueType.INVALID);
}

View File

@ -224,9 +224,9 @@ public class FHIRPathTests {
try {
if (Utilities.noString(input)) {
fp.check(null, null, node);
fp.check(null, null, null, node);
} else {
fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node);
fp.check(res, "Resource", res.getResourceType().toString(), res.getResourceType().toString(), node);
}
Assertions.assertTrue(fail != TestResultType.SEMANTICS,
String.format("Expected exception didn't occur checking %s", expression));

View File

@ -363,4 +363,19 @@ public class ComparisonRenderer implements IEvaluationContext {
return false;
}
public void loadTemplates(IWorkerContext context) {
getTemplates().put("CapabilityStatement", new String(context.getBinaryForKey("template-comparison-CapabilityStatement.html")));
getTemplates().put("CodeSystem-Intersection", new String(context.getBinaryForKey("template-comparison-CodeSystem-Intersection.html")));
getTemplates().put("CodeSystem-Union", new String(context.getBinaryForKey("template-comparison-CodeSystem-Union.html")));
getTemplates().put("CodeSystem", new String(context.getBinaryForKey("template-comparison-CodeSystem.html")));
getTemplates().put("Index", new String(context.getBinaryForKey("template-comparison-index.html")));
getTemplates().put("Profile-Intersection", new String(context.getBinaryForKey("template-comparison-Profile-Intersection.html")));
getTemplates().put("Profile-Union", new String(context.getBinaryForKey("template-comparison-Profile-Union.html")));
getTemplates().put("Profile", new String(context.getBinaryForKey("template-comparison-Profile.html")));
getTemplates().put("ValueSet-Intersection", new String(context.getBinaryForKey("template-comparison-ValueSet-Intersection.html")));
getTemplates().put("ValueSet-Union", new String(context.getBinaryForKey("template-comparison-ValueSet-Union.html")));
getTemplates().put("ValueSet", new String(context.getBinaryForKey("template-comparison-ValueSet.html")));
}
}

View File

@ -22,6 +22,7 @@ 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;
@ -30,6 +31,7 @@ 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.StructureDefinition;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.Utilities;
@ -479,5 +481,24 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
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 ("valueset-version".equals(p.getName())) {
String s = p.getValue().primitiveValue();
if (s.startsWith(value+"|")) {
return s;
}
}
}
return value;
}
}

View File

@ -111,6 +111,14 @@ public class Manager {
}
return null;
}
public static FhirFormat fromCode(String code) {
FhirFormat fmt = getFhirFormat(code);
if (fmt == null) {
fmt = readFromMimeType(code);
}
return fmt;
}
}
public static List<ValidatedFragment> parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {

View File

@ -485,8 +485,8 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, resourceType, context, expr, null);
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, rootResourceType, resourceType, context, expr, null);
}
/**
@ -501,7 +501,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, String context, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types;
@ -542,7 +542,7 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
}
/**
@ -557,7 +557,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails checkOnTypes(Object appContext, String resourceType, List<String> typeList, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, List<String> typeList, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
// if context is a path that refers to a type, do that conversion now
@ -607,21 +607,21 @@ public class FHIRPathEngine {
}
}
}
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, false, expr);
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, false, expr);
warnings.addAll(typeWarnings);
return res;
}
public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, false, expr);
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, false, expr);
warnings.addAll(typeWarnings);
return res;
}
public TypeDetails checkOnTypes(Object appContext, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings, boolean canBeNone) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails checkOnTypes(Object appContext, String rootResourceType, String resourceType, TypeDetails types, ExpressionNode expr, List<IssueMessage> warnings, boolean canBeNone) throws FHIRLexerException, PathEngineException, DefinitionException {
typeWarnings.clear();
TypeDetails res = executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, null, true, canBeNone, expr);
TypeDetails res = executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, null, true, canBeNone, expr);
warnings.addAll(typeWarnings);
return res;
}
@ -637,7 +637,7 @@ public class FHIRPathEngine {
* @throws PathEngineException
* @if the path is not valid
*/
public TypeDetails check(Object appContext, String resourceType, List<String> resourceTypes, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, List<String> resourceTypes, ExpressionNode expr, Set<ElementDefinition> elementDependencies) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types = null;
@ -649,7 +649,7 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, resourceType, types, types), types, expr, elementDependencies, true, false, expr);
}
private FHIRException makeExceptionPlural(Integer num, ExpressionNode holder, String constName, Object... args) {
@ -676,7 +676,7 @@ public class FHIRPathEngine {
}
}
public TypeDetails check(Object appContext, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types;
if (!context.contains(".")) {
@ -702,17 +702,17 @@ public class FHIRPathEngine {
}
}
return executeType(new ExecutionTypeContext(appContext, sd.getUrl(), types, types), types, expr, null, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, sd.getUrl(), types, types), types, expr, null, true, false, expr);
}
public TypeDetails check(Object appContext, StructureDefinition sd, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
public TypeDetails check(Object appContext, String rootResourceType, StructureDefinition sd, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException {
// if context is a path that refers to a type, do that conversion now
TypeDetails types = null; // this is a special case; the first path reference will have to resolve to something in the context
return executeType(new ExecutionTypeContext(appContext, sd == null ? null : sd.getUrl(), null, types), types, expr, null, true, false, expr);
return executeType(new ExecutionTypeContext(appContext, rootResourceType, sd == null ? null : sd.getUrl(), null, types), types, expr, null, true, false, expr);
}
public TypeDetails check(Object appContext, String resourceType, String context, String expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, resourceType, context, parse(expr));
public TypeDetails check(Object appContext, String rootResourceType, String resourceType, String context, String expr) throws FHIRLexerException, PathEngineException, DefinitionException {
return check(appContext, rootResourceType, resourceType, context, parse(expr));
}
private Integer compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
@ -1109,10 +1109,12 @@ public class FHIRPathEngine {
private TypeDetails thisItem;
private TypeDetails total;
private Map<String, TypeDetails> definedVariables;
private String rootResource;
public ExecutionTypeContext(Object appInfo, String resource, TypeDetails context, TypeDetails thisItem) {
public ExecutionTypeContext(Object appInfo, String rootResource, String resource, TypeDetails context, TypeDetails thisItem) {
super();
this.appInfo = appInfo;
this.rootResource = rootResource;
this.resource = resource;
this.context = context;
this.thisItem = thisItem;
@ -3224,10 +3226,10 @@ public class FHIRPathEngine {
}
return new TypeDetails(CollectionStatus.SINGLETON, context.resource);
} else if (s.equals("%rootResource")) {
if (context.resource == null) {
if (context.rootResource == null) {
throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus rootResource");
}
return new TypeDetails(CollectionStatus.SINGLETON, context.resource);
return new TypeDetails(CollectionStatus.SINGLETON, context.rootResource);
} else if (s.equals("%context")) {
return context.context;
} else if (s.equals("%map-codes")) {
@ -3863,6 +3865,18 @@ public class FHIRPathEngine {
}
private boolean typeCastIsImpossible(TypeDetails focus, String tn) {
for (ProfiledType pt : focus.getProfiledTypes()) {
if (!pt.hasProfiles()) { // get back to this later
String purl = tn;
while (purl != null && !purl.equals(pt.getUri())) {
StructureDefinition sd = worker.fetchResource(StructureDefinition.class, purl);
purl = sd == null ? null : sd.getBaseDefinition();
}
if (purl != null) {
return false;
}
}
}
return !focus.hasType(tn);
}
@ -4806,7 +4820,7 @@ public class FHIRPathEngine {
}
private ExecutionTypeContext changeThis(ExecutionTypeContext context, TypeDetails newThis) {
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, newThis);
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.rootResource, context.resource, context.context, newThis);
// append all of the defined variables from the context into the new context
if (context.definedVariables != null) {
for (String s : context.definedVariables.keySet()) {
@ -4817,7 +4831,7 @@ public class FHIRPathEngine {
}
private ExecutionTypeContext contextForParameter(ExecutionTypeContext context) {
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.resource, context.context, context.thisItem);
ExecutionTypeContext newContext = new ExecutionTypeContext(context.appInfo, context.rootResource, context.resource, context.context, context.thisItem);
// append all of the defined variables from the context into the new context
if (context.definedVariables != null) {
for (String s : context.definedVariables.keySet()) {
@ -6314,6 +6328,12 @@ public class FHIRPathEngine {
if (ed.getPath().startsWith(path)) {
if (ed.hasContentReference()) {
String cpath = ed.getContentReference();
if (!cpath.startsWith("#")) {
if (!cpath.contains("#")) {
throw new Error("ContentReference doesn't contain a #: "+cpath);
}
cpath = cpath.substring(cpath.indexOf("#"));
}
String tn = sdi.getType()+cpath;
if (!result.hasType(worker, tn)) {
if (elementDependencies != null) {

View File

@ -0,0 +1,64 @@
package org.hl7.fhir.r5.profilemodel;
import java.util.Map;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ResourceFactory;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;
import org.hl7.fhir.utilities.Utilities;
public class TestInstanceGenerator {
private IWorkerContext context;
private Map<String, String> data;
protected TestInstanceGenerator(IWorkerContext context) {
super();
this.context = context;
}
public Resource generate(StructureDefinition profile) {
PEBuilder builder = new PEBuilder(context, PEElementPropertiesPolicy.NONE, true);
PEDefinition definition = builder.buildPEDefinition(profile);
Resource res = ResourceFactory.createResource(definition.types().get(0).getType());
populateByProfile(res, definition);
res.getMeta().addProfile(definition.profile.getVersionedUrl());
return res;
}
protected void populateByProfile(Base base, PEDefinition definition) {
if (definition.types().size() == 1) {
for (PEDefinition pe : definition.directChildren(true)) {
if (pe.hasFixedValue()) {
if (pe.definition().hasPattern()) {
base.setProperty(pe.schemaName(), pe.definition().getPattern());
} else {
base.setProperty(pe.schemaName(), pe.definition().getFixed());
}
} else if (!pe.isSlicer() && pe.max() == 1) {
for (int i = 0; i < pe.min(); i++) {
Base b = null;
if (pe.schemaName().endsWith("[x]")) {
if (pe.types().size() == 1) {
b = base.addChild(pe.schemaName().replace("[x]", Utilities.capitalize(pe.types().get(0).getType())));
}
} else if (!pe.isBaseList()) {
b = base.makeProperty(pe.schemaName().hashCode(), pe.schemaName());
} else {
b = base.addChild(pe.schemaName());
}
if (b != null) {
populateByProfile(b, pe);
}
}
}
}
}
}
}

View File

@ -41,7 +41,6 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;

View File

@ -3,14 +3,12 @@ package org.hl7.fhir.r5.renderers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
@ -20,7 +18,6 @@ import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.SourcedElementDefinition;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper.NamedResourceWrapperList;

View File

@ -33,7 +33,6 @@ import org.hl7.fhir.utilities.xhtml.NodeType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
public class QuestionnaireRenderer extends TerminologyRenderer {
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = "http://hl7.org/fhir/4.0/StructureDefinition/extension-questionnaire.item.type";
public QuestionnaireRenderer(RenderingContext context) {
super(context);
@ -289,9 +288,9 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
String txt = (i.has("prefix") ? i.primitiveValue("prefix") + ". " : "") + i.primitiveValue("text");
r.getCells().add(gen.new Cell(null, null, txt, null, null));
r.getCells().add(gen.new Cell(null, null, ("true".equals(i.primitiveValue("required")) ? "1" : "0")+".."+("true".equals(i.primitiveValue("repeats")) ? "*" : "1"), null, null));
if (i.child("type").hasExtension(EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL)) {
if (i.child("type").hasExtension(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL)) {
status.setExtensions(true);
String t = i.child("type").extensionString(EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL);
String t = i.child("type").extensionString(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL);
r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+t, t, null, null));
} else {
r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+type, type, null, null));

View File

@ -26,7 +26,6 @@ import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceReferenceKind;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference;
@ -603,6 +602,10 @@ public abstract class ResourceRenderer extends DataRenderer {
if (v.startsWith("mailto:")) {
x.ah(v).addText(v.substring(7));
} else {
String link = getLinkForCode(v, null, null);
if (link != null) {
x.ah(context.prefixLocalHref(link)).addText(v);
} else {
ResourceWithReference rr = local ? resolveReference(uri.resource(), v, true) : resolveReference(uri);
if (rr != null) {
if (rr.getResource() == null) {
@ -628,6 +631,7 @@ public abstract class ResourceRenderer extends DataRenderer {
}
}
}
}
}
}

View File

@ -79,11 +79,10 @@ import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.LanguageUtils;
import org.hl7.fhir.r5.extensions.ExtensionConstants;
import org.hl7.fhir.r5.extensions.Extensions;
import org.hl7.fhir.r5.extensions.ExtensionsUtils;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
@ -93,6 +92,7 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.DecimalType;
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Factory;
@ -103,7 +103,6 @@ import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
@ -117,7 +116,6 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander.Token;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProviderExtension;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext;
@ -132,7 +130,6 @@ import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader;
import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader.LanguagePreference;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationOptions;
public class ValueSetExpander extends ValueSetProcessBase {
@ -160,6 +157,8 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private static final boolean REPORT_VERSION_ANYWAY = true;
private static final String VS_EXP_IMPORT_ERROR_TOO_COSTLY = null;
private ValueSet focus;
private List<String> allErrors = new ArrayList<>();
@ -864,13 +863,16 @@ public class ValueSetExpander extends ValueSetProcessBase {
private ValueSet importValueSet(WorkingContext wc, String value, ValueSetExpansionComponent exp, Parameters expParams, boolean noInactive, ValueSet valueSet) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
if (value == null)
throw fail("unable to find value set with no identity");
ValueSet vs = context.findTxResource(ValueSet.class, value, valueSet);
throw fail(I18nConstants.VS_EXP_IMPORT_NULL, true);
String url = getCu().pinValueSet(value, expParams);
ValueSet vs = context.findTxResource(ValueSet.class, url, valueSet);
if (vs == null) {
if (context.fetchResource(CodeSystem.class, value, valueSet) != null) {
throw fail("Cannot include value set "+value+" because it's actually a code system");
} else {
throw fail("Unable to find imported value set " + value);
boolean pinned = !url.equals(value);
String ver = pinned ? url.substring(value.length()+1) : null;
if (context.fetchResource(CodeSystem.class, url, valueSet) != null) {
throw fail(pinned ? I18nConstants.VS_EXP_IMPORT_CS_PINNED : I18nConstants.VS_EXP_IMPORT_CS, true, value, ver);
} else {
throw fail(pinned ? I18nConstants.VS_EXP_IMPORT_UNK_PINNED : I18nConstants.VS_EXP_IMPORT_UNK, true, value, ver);
}
}
checkCanonical(exp, vs, focus);
@ -881,9 +883,9 @@ public class ValueSetExpander extends ValueSetProcessBase {
ValueSetExpansionOutcome vso = new ValueSetExpander(context, opContext.copy(), allErrors).expand(vs, expParams);
if (vso.getError() != null) {
addErrors(vso.getAllErrors());
throw fail("Unable to expand imported value set "+vs.getUrl()+": " + vso.getError());
throw fail(I18nConstants.VS_EXP_IMPORT_ERROR, true, vs.getUrl(), vso.getError());
} else if (vso.getValueset() == null) {
throw fail("Unable to expand imported value set "+vs.getUrl()+" but no error");
throw fail(I18nConstants.VS_EXP_IMPORT_FAIL, true, vs.getUrl());
}
if (vs.hasVersion() || REPORT_VERSION_ANYWAY) {
UriType u = new UriType(vs.getUrl() + (vs.hasVersion() ? "|"+vs.getVersion() : ""));
@ -916,15 +918,19 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private ValueSet importValueSetForExclude(WorkingContext wc, String value, ValueSetExpansionComponent exp, Parameters expParams, boolean noInactive, ValueSet valueSet) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
if (value == null)
throw fail("unable to find value set with no identity");
ValueSet vs = context.findTxResource(ValueSet.class, value, valueSet);
throw fail(I18nConstants.VS_EXP_IMPORT_NULL_X, true);
String url = getCu().pinValueSet(value, expParams);
ValueSet vs = context.findTxResource(ValueSet.class, url, valueSet);
if (vs == null) {
if (context.fetchResource(CodeSystem.class, value, valueSet) != null) {
throw fail("Cannot include value set "+value+" because it's actually a code system");
} else {
throw fail("Unable to find imported value set " + value);
boolean pinned = !url.equals(value);
String ver = pinned ? url.substring(value.length()+1) : null;
if (context.fetchResource(CodeSystem.class, url, valueSet) != null) {
throw fail(pinned ? I18nConstants.VS_EXP_IMPORT_CS_PINNED_X : I18nConstants.VS_EXP_IMPORT_CS_X, true, value, ver);
} else {
throw fail(pinned ? I18nConstants.VS_EXP_IMPORT_UNK_PINNED_X : I18nConstants.VS_EXP_IMPORT_UNK_X, true, value, ver);
}
}
checkCanonical(exp, vs, focus);
@ -935,8 +941,11 @@ public class ValueSetExpander extends ValueSetProcessBase {
ValueSetExpansionOutcome vso = new ValueSetExpander(context, opContext.copy(), allErrors).expand(vs, expParams);
if (vso.getError() != null) {
addErrors(vso.getAllErrors());
throw fail("Unable to expand imported value set "+vs.getUrl()+": " + vso.getError());
throw fail(I18nConstants.VS_EXP_IMPORT_ERROR_X, true, vs.getUrl(), vso.getError());
} else if (vso.getValueset() == null) {
throw fail(I18nConstants.VS_EXP_IMPORT_FAIL_X, true, vs.getUrl());
}
if (vs.hasVersion() || REPORT_VERSION_ANYWAY) {
UriType u = new UriType(vs.getUrl() + (vs.hasVersion() ? "|"+vs.getVersion() : ""));
if (!existsInParams(exp.getParameter(), "used-valueset", u))
@ -944,7 +953,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
for (Extension ex : vso.getValueset().getExpansion().getExtension()) {
if (ex.getUrl().equals(ToolingExtensions.EXT_EXP_TOOCOSTLY)) {
throw fail("Unable to expand imported value set "+vs.getUrl()+" for exclude: too costly");
throw fail(VS_EXP_IMPORT_ERROR_TOO_COSTLY, true, vs.getUrl());
}
}
return vso.getValueset();
@ -1252,7 +1261,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
}
} else {
throw fail("Filter by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
throw fail(I18nConstants.VS_EXP_FILTER_UNK, true, focus.getVersionedUrl(), fc.getProperty(), fc.getOp());
}
}
@ -1313,7 +1322,8 @@ public class ValueSetExpander extends ValueSetProcessBase {
return key(c.getSystem(), c.getCode());
}
private FHIRException fail(String msg) {
private FHIRException fail(String msgId, boolean check, Object... params) {
String msg = context.formatMessage(msgId, params);
allErrors.add(msg);
return new FHIRException(msg);
}

View File

@ -25,6 +25,7 @@ public class ValidationResult {
private boolean inactive;
private String status;
private String server;
private boolean errorIsDisplayIssue;
@Override
public String toString() {
@ -110,7 +111,7 @@ public class ValidationResult {
}
public boolean isOk() {
return severity == null || severity == IssueSeverity.INFORMATION || severity == IssueSeverity.WARNING;
return severity == null || severity == IssueSeverity.INFORMATION || severity == IssueSeverity.WARNING || errorIsDisplayIssue;
}
public String getSystem() {
@ -408,4 +409,15 @@ public class ValidationResult {
}
return true;
}
public boolean isErrorIsDisplayIssue() {
return errorIsDisplayIssue;
}
public ValidationResult setErrorIsDisplayIssue(boolean errorIsDisplayIssue) {
this.errorIsDisplayIssue = errorIsDisplayIssue;
return this;
}
}

View File

@ -3,6 +3,7 @@ package org.hl7.fhir.r5.terminologies.utilities;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CanonicalResource;
@ -48,6 +49,7 @@ public class ValueSetProcessBase {
}
}
protected IWorkerContext context;
private ContextUtilities cu;
protected TerminologyOperationContext opContext;
protected List<String> requiredSupplements = new ArrayList<>();
@ -236,6 +238,14 @@ public class ValueSetProcessBase {
}
public ContextUtilities getCu() {
if (cu == null) {
cu = new ContextUtilities(context);
}
return cu;
}
protected AlternateCodesProcessingRules altCodeParams = new AlternateCodesProcessingRules(false);
protected AlternateCodesProcessingRules allAltCodes = new AlternateCodesProcessingRules(true);
}

View File

@ -133,7 +133,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
private ValidationOptions options;
private ValidationContextCarrier localContext;
private List<CodeSystem> localSystems = new ArrayList<>();
protected Parameters expansionProfile;
protected Parameters expansionParameters;
private TerminologyClientManager tcm;
private Set<String> unknownSystems;
private Set<String> unknownValueSets = new HashSet<>();
@ -143,7 +143,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
super(context, opContext);
this.valueset = source;
this.options = options;
this.expansionProfile = expansionProfile;
this.expansionParameters = expansionProfile;
this.tcm = tcm;
analyseValueSet();
}
@ -154,7 +154,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
this.options = options.copy();
this.options.setEnglishOk(true);
this.localContext = ctxt;
this.expansionProfile = expansionProfile;
this.expansionParameters = expansionProfile;
this.tcm = tcm;
analyseValueSet();
}
@ -200,7 +200,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
opContext.note("vs = null");
}
altCodeParams.seeParameters(expansionProfile);
altCodeParams.seeParameters(expansionParameters);
altCodeParams.seeValueSet(valueset);
if (localContext != null) {
if (valueset != null) {
@ -456,7 +456,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
private int checkValueSetLoad(ConceptSetComponent inc, ValidationProcessInfo info) {
int serverCount = 0;
for (UriType uri : inc.getValueSet()) {
ValueSetValidator vsv = getVs(uri.getValue(), info);
String url = getCu().pinValueSet(uri.getValue(), expansionParameters);
ValueSetValidator vsv = getVs(url, info);
serverCount += vsv.getServerLoad(info);
}
CodeSystem cs = resolveCodeSystem(inc.getSystem(), inc.getVersion());
@ -1005,6 +1006,9 @@ public class ValueSetValidator extends ValueSetProcessBase {
String msg = context.formatMessagePlural(options.getLanguages().getLangs().size(), none ? I18nConstants.NO_VALID_DISPLAY_FOUND_LANG_NONE : I18nConstants.NO_VALID_DISPLAY_FOUND_LANG_SOME, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary(), code.getDisplay());
String n = null;
return new ValidationResult(IssueSeverity.INFORMATION, n, code.getSystem(), cs.getVersion(), cc, getPreferredDisplay(cc, cs), makeIssue(IssueSeverity.INFORMATION, IssueType.INVALID, path+".display", msg, OpIssueCode.DisplayComment, null)).setStatus(inactive, status);
} else if (!code.getDisplay().equals(vc.getDisplay())) {
String msg = context.formatMessage(I18nConstants.NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR, code.getDisplay(), code.getSystem(), code.getCode(), options.langSummary(), vc.getDisplay());
return new ValidationResult(IssueSeverity.ERROR, msg, code.getSystem(), cs.getVersion(), cc, cc.getDisplay(), makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path+".display", msg, OpIssueCode.Display, null)).setStatus(inactive, status).setErrorIsDisplayIssue(true);
} else {
String msg = context.formatMessagePlural(options.getLanguages().getLangs().size(), I18nConstants.NO_VALID_DISPLAY_FOUND, code.getSystem(), code.getCode(), code.getDisplay(), options.langSummary());
return new ValidationResult(IssueSeverity.WARNING, msg, code.getSystem(), cs.getVersion(), cc, cc.getDisplay(), makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path+".display", msg, OpIssueCode.Display, null)).setStatus(inactive, status);
@ -1108,7 +1112,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
}
for (CanonicalType url : inc.getValueSet()) {
ConceptReferencePair cc = getVs(url.asStringValue(), null).findValueSetRef(system, code);
ConceptReferencePair cc = getVs(getCu().pinValueSet(url.asStringValue(), expansionParameters), null).findValueSetRef(system, code);
if (cc != null) {
return cc;
}
@ -1221,7 +1225,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
private boolean scanForCodeInValueSetInclude(String code, Set<String> sys, List<StringWithCode> problems, int i, ConceptSetComponent vsi) {
if (vsi.hasValueSet()) {
for (CanonicalType u : vsi.getValueSet()) {
if (!checkForCodeInValueSet(code, u.getValue(), sys, problems)) {
if (!checkForCodeInValueSet(code, getCu().pinValueSet(u.getValue(), expansionParameters), sys, problems)) {
return false;
}
}
@ -1379,19 +1383,19 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isValueSetUnionImports()) {
ok = false;
for (UriType uri : vsi.getValueSet()) {
if (inImport(path, uri.getValue(), system, version, code, info)) {
if (inImport(path, getCu().pinValueSet(uri.getValue(), expansionParameters), system, version, code, info)) {
return true;
}
}
} else {
Boolean bok = inImport(path, vsi.getValueSet().get(0).getValue(), system, version, code, info);
Boolean bok = inImport(path, getCu().pinValueSet(vsi.getValueSet().get(0).getValue(), expansionParameters), system, version, code, info);
if (bok == null) {
return bok;
}
ok = bok;
for (int i = 1; i < vsi.getValueSet().size(); i++) {
UriType uri = vsi.getValueSet().get(i);
ok = ok && inImport(path, uri.getValue(), system, version, code, info);
ok = ok && inImport(path, getCu().pinValueSet(uri.getValue(), expansionParameters), system, version, code, info);
}
}
}
@ -1662,7 +1666,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
unknownValueSets.add(url);
info.addIssue(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, null, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_VALUE_SET_, url), OpIssueCode.NotFound, null));
}
ValueSetValidator vsc = new ValueSetValidator(context, opContext.copy(), options, vs, localContext, expansionProfile, tcm);
ValueSetValidator vsc = new ValueSetValidator(context, opContext.copy(), options, vs, localContext, expansionParameters, tcm);
vsc.setThrowToServer(throwToServer);
inner.put(url, vsc);
return vsc;

View File

@ -63,8 +63,8 @@ public class VersionInfo {
}
private String getVersionParameter(String name, String system) {
if (this.valueSetCheckerSimple.expansionProfile != null) {
for (ParametersParameterComponent pc : this.valueSetCheckerSimple.expansionProfile.getParameter()) {
if (this.valueSetCheckerSimple.expansionParameters != null) {
for (ParametersParameterComponent pc : this.valueSetCheckerSimple.expansionParameters.getParameter()) {
if (name.equals(pc.getName()) && pc.hasValue()) {
String v = pc.getValue().primitiveValue();
if (v != null && v.startsWith(system+"|")) {

View File

@ -237,6 +237,7 @@ public class ToolingExtensions {
public static final String EXT_JSON_PRIMITIVE_CHOICE = "http://hl7.org/fhir/tools/StructureDefinition/json-primitive-choice";
public static final String EXT_SUMMARY = "http://hl7.org/fhir/StructureDefinition/structuredefinition-summary";
public static final String EXT_BINDING_DEFINITION = "http://hl7.org/fhir/tools/StructureDefinition/binding-definition";
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = "http://hl7.org/fhir/4.0/StructureDefinition/extension-questionnaire.item.type";
// unregistered? - don't know what these are used for

View File

@ -291,7 +291,7 @@ public class Validator {
try {
node = fpe.parse(expr);
column.setUserData(UserDataNames.db_path, node);
td = fpe.checkOnTypes(vd, resourceName, t, node, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, node, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -473,7 +473,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
focus.setUserData(UserDataNames.db_forEach, n);
td = fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -498,7 +498,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
focus.setUserData(UserDataNames.db_forEachOrNull, n);
td = fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
error(path, expression, e.getMessage(), IssueType.INVALID);
}
@ -611,7 +611,7 @@ public class Validator {
try {
ExpressionNode n = fpe.parse(expr);
where.setUserData(UserDataNames.db_path, n);
td = fpe.checkOnTypes(vd, resourceName, types, n, warnings);
td = fpe.checkOnTypes(vd, "Resource", resourceName, types, n, warnings);
} catch (Exception e) {
error(path, where.get("path"), e.getMessage(), IssueType.INVALID);
}

View File

@ -2531,7 +2531,7 @@ public class StructureMapUtilities {
if (expr == null) {
expr = fpe.parse(getParamString(vars, tgt.getParameter().get(tgt.getParameter().size() - 1)));
}
return fpe.check(vars, null, expr);
return fpe.check(vars, null, null, expr);
case TRANSLATE:
return new TypeDetails(CollectionStatus.SINGLETON, "CodeableConcept");
case CC:

View File

@ -219,9 +219,9 @@ public class FHIRPathTests {
if (!skipStaticCheck) {
try {
if (Utilities.noString(input)) {
fp.check(null, null, node);
fp.check(null, null, null, node);
} else {
fp.check(res, res.fhirType(), res.fhirType(), node);
fp.check(res, res.fhirType(), res.fhirType(), res.fhirType(), node);
}
Assertions.assertTrue(fail != TestResultType.SEMANTICS, String.format("Expected exception didn't occur checking %s", expression));
} catch (Exception e) {

View File

@ -1147,4 +1147,20 @@ public class I18nConstants {
public static final String HTA_SCT_MESSAGE = "HTA_SCT_MESSAGE";
public static final String HTA_LOINC_DESC = "HTA_LOINC_DESC";
public static final String HTA_LOINC_MESSAGE = "HTA_LOINC_MESSAGE";
public static final String VS_EXP_IMPORT_CS_PINNED = "VS_EXP_IMPORT_CS_PINNED";
public static final String VS_EXP_IMPORT_CS = "VS_EXP_IMPORT_CS";
public static final String VS_EXP_IMPORT_UNK_PINNED = "VS_EXP_IMPORT_UNK_PINNED";
public static final String VS_EXP_IMPORT_UNK = "VS_EXP_IMPORT_UNK";
public static final String VS_EXP_IMPORT_NULL = "VS_EXP_IMPORT_NULL";
public static final String VS_EXP_IMPORT_ERROR = "VS_EXP_IMPORT_ERROR";
public static final String VS_EXP_IMPORT_FAIL = "VS_EXP_IMPORT_FAIL";
public static final String VS_EXP_IMPORT_NULL_X = "VS_EXP_IMPORT_NULL_X";
public static final String VS_EXP_IMPORT_CS_PINNED_X = "VS_EXP_IMPORT_CS_PINNED_X";
public static final String VS_EXP_IMPORT_CS_X = "VS_EXP_IMPORT_CS_X";
public static final String VS_EXP_IMPORT_UNK_PINNED_X = "VS_EXP_IMPORT_UNK_PINNED_X";
public static final String VS_EXP_IMPORT_UNK_X = "VS_EXP_IMPORT_UNK_X";
public static final String VS_EXP_IMPORT_ERROR_X = "VS_EXP_IMPORT_ERROR_X";
public static final String VS_EXP_IMPORT_FAIL_X = "VS_EXP_IMPORT_FAIL_X";
public static final String VS_EXP_FILTER_UNK = "VS_EXP_FILTER_UNK";
public static final String NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR = "NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR";
}

View File

@ -758,6 +758,9 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
} else if (id.startsWith("hl7.fhir.r6")) {
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL("https://build.fhir.org", id + ".tgz"), false);
return new InputStreamWithSrc(stream, Utilities.pathURL("https://build.fhir.org", id + ".tgz"), "current");
} else if (id.startsWith("hl7.fhir.uv.extensions.")) {
InputStream stream = fetchFromUrlSpecific(Utilities.pathURL("https://build.fhir.org/ig/HL7/fhir-extensions/", id + ".tgz"), false);
return new InputStreamWithSrc(stream, Utilities.pathURL("https://build.fhir.org/ig/HL7/fhir-extensions/", id + ".tgz"), "current");
} else {
throw new FHIRException("The package '" + id + "' has no entry on the current build server (" + ciList + ")");
}

View File

@ -1180,3 +1180,20 @@ HTA_SCT_MESSAGE = This material contains content that is copyright of SNOMED Int
HTA_LOINC_DESC = LOINC
HTA_LOINC_MESSAGE = This material contains content from <a href=\"http://loinc.org\">LOINC</a>. LOINC is copyright &copy; 1995-2020, Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and is available at no cost under the <a href=\"http://loinc.org/license\">license</a>. LOINC&reg; is a registered United States trademark of Regenstrief Institute, Inc.
SD_PATH_NO_SLICING = Slicing is not allowed at ''{0}''
VS_EXP_IMPORT_CS = Cannot include value set ''{0}'' because it's actually a code system
VS_EXP_IMPORT_CS_PINNED = Cannot include value set ''{0}'' version ''{1}'' because it's actually a code system
VS_EXP_IMPORT_UNK = Unable to find included value set ''{0}''
VS_EXP_IMPORT_UNK_PINNED = Unable to find included value set ''{0}'' version ''{1}''
VS_EXP_IMPORT_NULL = Unable to find included value set with no identity
VS_EXP_IMPORT_ERROR = Unable to expand included value set ''{0}'': {1}
VS_EXP_IMPORT_ERROR = Unable to expand included value set ''{0}'', but no error
VS_EXP_IMPORT_CS_X = Cannot exclude value set ''{0}'' because it's actually a code system
VS_EXP_IMPORT_CS_PINNED_X = Cannot exclude value set ''{0}'' version ''{1}'' because it's actually a code system
VS_EXP_IMPORT_UNK_X = Unable to find excluded value set ''{0}''
VS_EXP_IMPORT_UNK_PINNED_X = Unable to find excluded value set ''{0}'' version ''{1}''
VS_EXP_IMPORT_NUL_XL = Unable to find excluded value set with no identity
VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'': {1}
VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'', but no error
VS_EXP_IMPORT_ERROR_TOO_COSTLY = Unable to expand excluded value set ''{0}'': too costly
VS_EXP_FILTER_UNK = ValueSet ''{0}'' Filter by property ''{1}'' and op ''{2}'' is not supported yet

View File

@ -195,10 +195,12 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
protected BestPracticeWarningLevel bpWarnings = BestPracticeWarningLevel.Warning; // @configuration
protected List<UsageContext> usageContexts = new ArrayList<UsageContext>(); // @configuration
protected ValidationOptions baseOptions = new ValidationOptions(FhirPublication.R5); // @configuration
protected ContextUtilities cu;
public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, ValidatorSession session) {
super();
this.context = context;
cu = new ContextUtilities(context);
this.session = session;
if (this.session == null) {
this.session = new ValidatorSession();
@ -217,6 +219,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
this.parent = parent;
this.session = parent.session;
this.context = parent.context;
this.cu = parent.cu;
this.xverManager = parent.xverManager;
this.debug = parent.debug;
this.source = parent.source;
@ -955,6 +958,7 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
}
return null;
} else {
reference = cu.pinValueSet(reference);
long t = System.nanoTime();
ValueSet fr = context.findTxResource(ValueSet.class, reference, src);
if (fr == null) {
@ -1160,9 +1164,11 @@ public class BaseValidator implements IValidationContextResourceLoader, IMessagi
if (bnd != null) {
// in this case, we look into the parent - if there is one - and if it's a bundle, we look at the entries (but not in them)
for (Element be : bnd.getChildrenByName("entry")) {
String id = be.getIdBase();
if (fragment.equals(id)) {
count++;
if (be.hasChild("resource")) {
String id = be.getNamedChild("resource").getIdBase();
if (fragment.equals(id)) {
count++;
}
}
}
}

View File

@ -2,6 +2,7 @@ package org.hl7.fhir.validation;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@ -1295,4 +1296,24 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
return ReferenceValidationPolicy.IGNORE;
}
public void loadExpansionParameters(String expansionParameters) {
System.out.println("Load Expansion Parameters: "+expansionParameters);
Parameters p = null;
try {
p = (Parameters) new XmlParser().parse(new FileInputStream(expansionParameters));
} catch (Exception e) {
}
if (p == null) {
try {
p = (Parameters) new JsonParser().parse(new FileInputStream(expansionParameters));
} catch (Exception e) {
System.out.println("Unable to load expansion parameters '"+expansionParameters+"' as either xml or json: "+e.getMessage());
throw new FHIRException("Unable to load expansion parameters '"+expansionParameters+"' as either xml or json: "+e.getMessage());
}
}
context.setExpansionParameters(p);
}
}

View File

@ -137,6 +137,7 @@ public class ValidatorCli {
new TransformTask(),
new VersionTask(),
new CodeGenTask(),
new TxPackTask(),
defaultCliTask);
}

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.validation.cli.model;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -8,6 +9,11 @@ import java.util.Map;
import java.util.Objects;
import com.google.gson.annotations.SerializedName;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.utils.validation.BundleValidationRule;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
@ -322,6 +328,16 @@ public class CliContext {
private
String advisorFile;
@JsonProperty("expansionParameters")
@SerializedName("expansionParameters")
private
String expansionParameters;
@JsonProperty("format")
@SerializedName("format")
private
FhirFormat format;
@SerializedName("baseEngine")
@JsonProperty("baseEngine")
public String getBaseEngine() {
@ -1146,6 +1162,8 @@ public class CliContext {
Objects.equals(unknownCodeSystemsCauseErrors, that.unknownCodeSystemsCauseErrors) &&
Objects.equals(noExperimentalContent, that.noExperimentalContent) &&
Objects.equals(advisorFile, that.advisorFile) &&
Objects.equals(expansionParameters, that.expansionParameters) &&
Objects.equals(format, that.format) &&
Objects.equals(watchSettleTime, that.watchSettleTime);
}
@ -1154,7 +1172,7 @@ public class CliContext {
return Objects.hash(baseEngine, doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching,
noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT,
targetVer, packageName, igs, questionnaireMode, level, profiles, options, sources, inputs, mode, locale, locations, crumbTrails, showMessageIds, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars,
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, advisorFile, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
watchMode, watchScanDelay, watchSettleTime, bestPracticeLevel, unknownCodeSystemsCauseErrors, noExperimentalContent, advisorFile, expansionParameters, format, htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath, checkIPSCodes);
}
@Override
@ -1219,6 +1237,8 @@ public class CliContext {
", unknownCodeSystemsCauseErrors=" + unknownCodeSystemsCauseErrors +
", noExperimentalContent=" + noExperimentalContent +
", advisorFile=" + advisorFile +
", expansionParameters=" + expansionParameters +
", format=" + format +
'}';
}
@ -1325,5 +1345,28 @@ public class CliContext {
this.advisorFile = advisorFile;
}
@SerializedName("expansionParameters")
@JsonProperty("expansionParameters")
public String getExpansionParameters() {
return expansionParameters;
}
@SerializedName("expansionParameters")
@JsonProperty("expansionParameters")
public void setExpansionParameters(String expansionParameters) {
this.expansionParameters = expansionParameters;
}
@SerializedName("format")
@JsonProperty("format")
public FhirFormat getFormat() {
return format;
}
@SerializedName("format")
@JsonProperty("format")
public void setFormat(FhirFormat format) {
this.format = format;
}
}

View File

@ -0,0 +1,83 @@
package org.hl7.fhir.validation.cli.tasks;
import java.io.File;
import java.io.PrintStream;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.utils.Display;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import org.hl7.fhir.validation.special.ExpansionPackageGenerator;
import org.hl7.fhir.validation.special.ExpansionPackageGenerator.ExpansionPackageGeneratorOutputType;
import org.hl7.fhir.validation.special.ExpansionPackageGenerator.ExpansionPackageGeneratorScope;
public class TxPackTask extends ValidationEngineTask {
@Override
public String getName() {
return "tx-pack";
}
@Override
public String getDisplayName() {
return "Generate a terminology pack";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
return cliContext.getMode() == EngineMode.TX_PACK;
}
@Override
public void printHelp(PrintStream out) {
Display.displayHelpDetails(out,"help/tx-pack.txt");
}
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
String pid = cliContext.getPackageName();
boolean json = cliContext.getFormat() != FhirFormat.XML;
String output = cliContext.getOutput();
File f = new File(output);
ExpansionPackageGeneratorOutputType t = ExpansionPackageGeneratorOutputType.FOLDER;
if (f.exists() && f.isDirectory()) {
t = ExpansionPackageGeneratorOutputType.FOLDER;
} else if (output.endsWith(".zip")) {
t = ExpansionPackageGeneratorOutputType.ZIP;
} else if (output.endsWith(".tgz")) {
t = ExpansionPackageGeneratorOutputType.TGZ;
}
ExpansionPackageGeneratorScope scope = ExpansionPackageGeneratorScope.IG_ONLY;
int c = -1;
for (int i = 0; i < args.length; i++) {
if ("-scope".equals(args[i])) {
c = i;
}
}
if (c < args.length - 1) {
switch (args[c+1].toLowerCase()) {
case "ig" : scope = ExpansionPackageGeneratorScope.IG_ONLY;
case "igs" : scope = ExpansionPackageGeneratorScope.ALL_IGS;
case "core" : scope = ExpansionPackageGeneratorScope.EVERYTHING;
default:
System.out.println("Unknown scope "+args[c+1]);
}
}
IWorkerContext ctxt = validationEngine.getContext();
ExpansionPackageGenerator ep = new ExpansionPackageGenerator().setContext(ctxt).setPackageId(pid).setScope(scope);
if (cliContext.getExpansionParameters() != null) {
validationEngine.loadExpansionParameters(cliContext.getExpansionParameters());
}
ep.setOutput(output).setOutputType(t);
ep.generateExpansionPackage();
}
}

View File

@ -51,6 +51,10 @@ public class ValidateTask extends ValidationEngineTask {
@Override
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
if (cliContext.getExpansionParameters() != null) {
validationEngine.loadExpansionParameters(cliContext.getExpansionParameters());
}
for (String s : cliContext.getProfiles()) {
if (!validationEngine.getContext().hasResource(StructureDefinition.class, s) && !validationEngine.getContext().hasResource(ImplementationGuide.class, s)) {
System.out.println(" Fetch Profile from " + s);

View File

@ -14,5 +14,6 @@ public enum EngineMode {
VERSION,
RUN_TESTS,
INSTALL,
CODEGEN
CODEGEN,
TX_PACK
}

View File

@ -6,6 +6,7 @@ import java.util.Arrays;
import java.util.Locale;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.utils.validation.BundleValidationRule;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
@ -45,13 +46,16 @@ public class Params {
public static final String EXTENSION = "-extension";
public static final String HINT_ABOUT_NON_MUST_SUPPORT = "-hintAboutNonMustSupport";
public static final String TO_VERSION = "-to-version";
public static final String TX_PACK = "-tx-pack";
public static final String PACKAGE_NAME = "-package-name";
public static final String DO_NATIVE = "-do-native";
public static final String NO_NATIVE = "-no-native";
public static final String COMPILE = "-compile";
public static final String CODEGEN = "-codegen";
public static final String TRANSFORM = "-transform";
public static final String FORMAT = "-format";
public static final String LANG_TRANSFORM = "-lang-transform";
public static final String EXP_PARAMS = "-expansion-parameters";
public static final String NARRATIVE = "-narrative";
public static final String SNAPSHOT = "-snapshot";
public static final String INSTALL = "-install";
@ -330,6 +334,9 @@ public class Params {
} else if (args[i].equals(PACKAGE_NAME)) {
cliContext.setPackageName(args[++i]);
cliContext.setMode(EngineMode.CODEGEN);
} else if (args[i].equals(TX_PACK)) {
cliContext.setPackageName(args[++i]);
cliContext.setMode(EngineMode.TX_PACK);
} else if (args[i].equals(DO_NATIVE)) {
cliContext.setCanDoNative(true);
} else if (args[i].equals(NO_NATIVE)) {
@ -337,9 +344,13 @@ public class Params {
} else if (args[i].equals(TRANSFORM)) {
cliContext.setMap(args[++i]);
cliContext.setMode(EngineMode.TRANSFORM);
} else if (args[i].equals(FORMAT)) {
cliContext.setFormat(FhirFormat.fromCode(args[++i]));
} else if (args[i].equals(LANG_TRANSFORM)) {
cliContext.setLangTransform(args[++i]);
cliContext.setMode(EngineMode.LANG_TRANSFORM);
} else if (args[i].equals(EXP_PARAMS)) {
cliContext.setExpansionParameters(args[++i]);
} else if (args[i].equals(COMPILE)) {
cliContext.setMap(args[++i]);
cliContext.setMode(EngineMode.COMPILE);

View File

@ -532,7 +532,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
return null;
}
return context.findTxResource(ValueSet.class, url);
return context.findTxResource(ValueSet.class, cu.pinValueSet(url));
}
@Override
@ -602,7 +602,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public boolean testMode;
private boolean example ;
private IDigitalSignatureServices signatureServices;
private ContextUtilities cu;
private boolean unknownCodeSystemsCauseErrors;
private boolean noExperimentalContent;
@ -611,7 +610,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
start = System.currentTimeMillis();
this.externalHostServices = hostServices;
this.profileUtilities = new ProfileUtilities(theContext, null, null);
cu = new ContextUtilities(theContext);
fpe = new FHIRPathEngine(context);
validatorServices = new ValidatorHostServices();
fpe.setHostServices(validatorServices);
@ -7739,7 +7737,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
n = fpe.parse(FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey(), context.getVersion()));
inv.setUserData(UserDataNames.validator_expression_cache, n);
}
fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n);
fpe.check(null, "Resource", sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n);
} catch (Exception e) {
System.out.println("Error processing structure [" + sd.getId() + "] path " + ed.getPath() + ":" + inv.getKey() + " ('" + inv.getExpression() + "'): " + e.getMessage());
}

View File

@ -26,6 +26,7 @@ import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -241,7 +242,11 @@ public class QuestionnaireValidator extends BaseValidator {
Element e = item.getNamedChild("type", false);
if (e != null) {
NodeStack ne = ns.push(e, -1, e.getProperty().getDefinition(), e.getProperty().getDefinition());
ok = rule(errors, "2023-06-15", IssueType.BUSINESSRULE, ne, qi.getType().toCode().equals(e.primitiveValue()), I18nConstants.QUESTIONNAIRE_Q_ITEM_DERIVED_NC_TYPE, derivation.questionnaire.getUrl(), linkId, qi.getType().toCode(), e.primitiveValue()) && ok;
String myType = qi.getType().toCode();
if (qi.getTypeElement().hasExtension(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL)) {
myType = qi.getTypeElement().getExtensionString(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL);
}
ok = rule(errors, "2023-06-15", IssueType.BUSINESSRULE, ne, myType.equals(e.primitiveValue()), I18nConstants.QUESTIONNAIRE_Q_ITEM_DERIVED_NC_TYPE, derivation.questionnaire.getUrl(), linkId, qi.getType().toCode(), e.primitiveValue()) && ok;
}
}

View File

@ -93,7 +93,7 @@ public class SearchParameterValidator extends BaseValidator {
boolean ok = true;
try {
List<IssueMessage> warnings = new ArrayList<>();
fpe.checkOnTypes(null, bases.size() == 1 ? bases.get(0) : "Resource", bases, fpe.parse(expression), warnings);
fpe.checkOnTypes(null, "Resource", bases.size() == 1 ? bases.get(0) : "Resource", bases, fpe.parse(expression), warnings);
for (IssueMessage m : warnings) {
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, m.getId(), false, m.getMessage());
}

View File

@ -479,7 +479,7 @@ public class StructureDefinitionValidator extends BaseValidator {
if (!td.isEmpty()) {
List<IssueMessage> warnings = new ArrayList<IssueMessage>();
try {
TypeDetails eval = fpe.checkOnTypes(this, tn, td, fpe.parse(pathExp), warnings, true);
TypeDetails eval = fpe.checkOnTypes(this, "Resource", tn, td, fpe.parse(pathExp), warnings, true);
if (eval.isEmpty()) {
ok = rule(errors, "2024-11-06", IssueType.INVALID, dStack, false, I18nConstants.SD_PATH_NOT_VALID, pathExp, path) && ok;
}
@ -744,16 +744,16 @@ public class StructureDefinitionValidator extends BaseValidator {
List<IssueMessage> warnings = new ArrayList<>();
ValidationContext vc = new ValidationContext(invariant);
if (Utilities.existsInList(rootPath, context.getResourceNames())) {
fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings);
fpe.checkOnTypes(vc, "Resource", rootPath, types, fpe.parse(exp), warnings);
} else {
StructureDefinition sd = context.fetchTypeDefinition(rootPath);
if (sd != null && sd.getKind() == StructureDefinitionKind.RESOURCE) {
fpe.checkOnTypes(vc, rootPath, types, fpe.parse(exp), warnings);
fpe.checkOnTypes(vc, "Resource", rootPath, types, fpe.parse(exp), warnings);
} else if (sd != null && sd.getKind() == StructureDefinitionKind.LOGICAL) {
String tn = ToolingExtensions.readStringExtension(sd, ToolingExtensions.EXT_LOGICAL_CONTAINER);
fpe.checkOnTypes(vc, tn == null ? rootPath : tn, types, fpe.parse(exp), warnings);
fpe.checkOnTypes(vc, "Resource", tn == null ? rootPath : tn, types, fpe.parse(exp), warnings);
} else {
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings);
fpe.checkOnTypes(vc, "Resource", "DomainResource", types, fpe.parse(exp), warnings);
}
}
for (IssueMessage s : warnings) {
@ -790,7 +790,7 @@ public class StructureDefinitionValidator extends BaseValidator {
try {
List<IssueMessage> warnings = new ArrayList<>();
ValidationContext vc = new ValidationContext(invariant);
fpe.checkOnTypes(vc, "DomainResource", types, fpe.parse(exp), warnings);
fpe.checkOnTypes(vc, "Resource", "DomainResource", types, fpe.parse(exp), warnings);
for (IssueMessage s : warnings) {
warning(errors, "2023-07-27", IssueType.BUSINESSRULE, stack, s.getId(), false, s.getMessage());
}

View File

@ -863,7 +863,7 @@ public class StructureMapValidator extends BaseValidator {
String exp = params.get(0).getChildValue("value");
if (rule(errors, "2023-03-01", IssueType.INVALID, params.get(0).line(), params.get(0).col(), stack.getLiteralPath(), exp != null, I18nConstants.SM_TARGET_TRANSFORM_PARAM_UNPROCESSIBLE, "0", params.size())) {
try {
TypeDetails td = fpe.check(variables, v.getSd().getUrl(), v.getEd().getPath(), fpe.parse(exp));
TypeDetails td = fpe.check(variables, null, v.getSd().getUrl(), v.getEd().getPath(), fpe.parse(exp));
if (td.getTypes().size() == 1) {
type = td.getType();
}

View File

@ -259,7 +259,7 @@ public class ViewDefinitionValidator extends BaseValidator {
List<IssueMessage> warnings = new ArrayList<>();
TypeDetails td = null;
try {
td = vec.fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = vec.fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
rule(errors, "2024-11-14", IssueType.EXCEPTION, stack, false, I18nConstants.VIEWDEFINITION_PATH_ERROR, e.getMessage(), vdesc);
ok = false;
@ -422,7 +422,7 @@ public class ViewDefinitionValidator extends BaseValidator {
List<IssueMessage> warnings = new ArrayList<>();
TypeDetails td = null;
try {
td = vec.fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = vec.fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
rule(errors, "2024-11-14", IssueType.EXCEPTION, stack, false, I18nConstants.VIEWDEFINITION_PATH_ERROR, e.getMessage(), vdesc);
return null;
@ -448,7 +448,7 @@ public class ViewDefinitionValidator extends BaseValidator {
List<IssueMessage> warnings = new ArrayList<>();
TypeDetails td = null;
try {
td = vec.fpe.checkOnTypes(vd, resourceName, t, n, warnings);
td = vec.fpe.checkOnTypes(vd, "Resource", resourceName, t, n, warnings);
} catch (Exception e) {
rule(errors, "2024-11-14", IssueType.EXCEPTION, stack, false, I18nConstants.VIEWDEFINITION_PATH_ERROR, e.getMessage(), vdesc);
return null;
@ -482,7 +482,7 @@ public class ViewDefinitionValidator extends BaseValidator {
types.add(resourceName);
TypeDetails td = null;
try {
td = vec.fpe.checkOnTypes(vd, resourceName, types, n, warnings);}
td = vec.fpe.checkOnTypes(vd, "Resource", resourceName, types, n, warnings);}
catch (Exception e) {
rule(errors, "2024-11-14", IssueType.EXCEPTION, stack, false, I18nConstants.VIEWDEFINITION_PATH_ERROR, e.getMessage(), vdesc);
ok = false;

View File

@ -0,0 +1,409 @@
package org.hl7.fhir.validation.special;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.NPMPackageGenerator;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.ZipGenerator;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.validation.IgLoader;
import org.hl7.fhir.validation.special.ExpansionPackageGenerator.TerminologyResourceEntry;
/**
* Given a package id, and an expansion parameters,
* get a list of all the value sets in the package, optionally with expansions.
* Parameters:
*
* - scope: this ig | all igs | all igs + core
* - expansions: true | false
* - output type: folder | zip | tgz
*/
public class ExpansionPackageGenerator {
public static class TerminologyResourceEntry {
public ValueSet valueSet;
public Set<String> sources = new HashSet<>();
public String error;
}
public static void main(String[] args) throws Exception {
new ExpansionPackageGenerator()
.setPackageId("hl7.fhir.us.davinci-alerts")
.setJson(true)
.setOutputType(ExpansionPackageGeneratorOutputType.TGZ)
.setOutput("/Users/grahamegrieve/temp/vs-output.tgz")
.generateExpansionPackage();
}
public enum ExpansionPackageGeneratorOutputType {
FOLDER, ZIP, TGZ
}
public enum ExpansionPackageGeneratorScope {
IG_ONLY, ALL_IGS, EVERYTHING
}
private String packageId;
private Parameters expansionParameters = new Parameters();
private ExpansionPackageGeneratorScope scope = ExpansionPackageGeneratorScope.EVERYTHING;
private boolean expansions;
private String output;
private ExpansionPackageGeneratorOutputType outputType;
private boolean hierarchical;
private boolean json;
private IWorkerContext context;
public String getPackageId() {
return packageId;
}
public ExpansionPackageGenerator setPackageId(String packageId) {
this.packageId = packageId;
return this;
}
public Parameters getExpansionParameters() {
return expansionParameters;
}
public ExpansionPackageGenerator setExpansionParameters(Parameters expansionParameters) {
this.expansionParameters = expansionParameters;
return this;
}
public ExpansionPackageGeneratorScope getScope() {
return scope;
}
public ExpansionPackageGenerator setScope(ExpansionPackageGeneratorScope scope) {
this.scope = scope;
return this;
}
public boolean isExpansions() {
return expansions;
}
public ExpansionPackageGenerator setExpansions(boolean expansions) {
this.expansions = expansions;
return this;
}
public String getOutput() {
return output;
}
public ExpansionPackageGenerator setOutput(String output) {
this.output = output;
return this;
}
public ExpansionPackageGeneratorOutputType getOutputType() {
return outputType;
}
public ExpansionPackageGenerator setOutputType(ExpansionPackageGeneratorOutputType outputType) {
this.outputType = outputType;
return this;
}
public boolean isHierarchical() {
return hierarchical;
}
public ExpansionPackageGenerator setHierarchical(boolean hierarchical) {
this.hierarchical = hierarchical;
return this;
}
public boolean isJson() {
return json;
}
public ExpansionPackageGenerator setJson(boolean json) {
this.json = json;
return this;
}
public IWorkerContext getContext() {
return context;
}
public ExpansionPackageGenerator setContext(IWorkerContext context) {
this.context = context;
return this;
}
private ContextUtilities cu;
private Map<String, TerminologyResourceEntry> entries = new HashMap<>();
public void generateExpansionPackage() throws IOException {
if (output == null) {
throw new Error("No output");
}
var npm = load();
System.out.println("Finding ValueSets");
for (String res : npm.listResources("StructureDefinition")) {
StructureDefinition sd = (StructureDefinition) new JsonParser().parse(npm.loadResource(res));
processSD(sd, npm.id());
}
System.out.println("Generating Expansions");
for (String n : Utilities.sorted(entries.keySet())) {
TerminologyResourceEntry e = entries.get(n);
try {
System.out.print("Generate Expansion for "+n+" ... ");
ValueSetExpansionOutcome exp = context.expandVS(e.valueSet, true, hierarchical);
if (exp.isOk()) {
e.valueSet.setExpansion(exp.getValueset().getExpansion());
System.out.println("OK");
} else {
e.valueSet.setExpansion(null);
e.error = exp.getError();
System.out.println(exp.getError());
}
} catch (Exception ex) {
System.out.println("Error= "+ex.getMessage());
e.error = ex.getMessage();
}
}
System.out.println("Producing Output");
switch (outputType) {
case FOLDER:
produceFolder();
break;
case TGZ:
producePackage(npm);
break;
case ZIP:
produceZip();
break;
default:
break;
}
System.out.println("Done");
}
private void produceZip() throws IOException {
ZipGenerator zip = new ZipGenerator(output);
Set<String> names = new HashSet<>();
names.add("manifest");
if (json) {
zip.addBytes("manifest.json", new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(expansionParameters), false);
} else {
zip.addBytes("manifest.xml", new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(expansionParameters), false);
}
StringBuilder b = new StringBuilder();
for (String n : Utilities.sorted(entries.keySet())) {
TerminologyResourceEntry e = entries.get(n);
String name = e.valueSet.getIdBase();
int i = 0;
while (names.contains(name)) {
i++;
name = e.valueSet.getIdBase()+i;
}
names.add(name);
if (e.error == null) {
b.append(name+","+n+", , "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
} else {
b.append(name+","+n+", \""+Utilities.escapeCSV(e.error)+"\", "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
}
if (json) {
zip.addBytes(name+".json", new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(e.valueSet), false);
} else {
zip.addBytes(name+".xml", new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(e.valueSet), false);
}
}
zip.addBytes("valuesets.csv", b.toString().getBytes(StandardCharsets.UTF_8), false);
zip.close();
}
private void producePackage(NpmPackage npm) throws FHIRException, IOException {
JsonObject j = new JsonObject();
j.add("name", npm.id()+".custom.extensions");
j.add("version", npm.version());
j.add("tools-version", 3);
j.add("type", "Conformance");
j.forceArray("fhirVersions").add(npm.fhirVersion());
j.forceObject("dependencies").add(VersionUtilities.packageForVersion(npm.fhirVersion()), npm.fhirVersion());
NPMPackageGenerator gen = new NPMPackageGenerator(output, j, new Date(), true);
Set<String> names = new HashSet<>();
names.add("manifest");
if (json) {
gen.addFile("package", "manifest.json", new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(expansionParameters));
} else {
gen.addFile("package", "manifest.xml", new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(expansionParameters));
}
StringBuilder b = new StringBuilder();
for (String n : Utilities.sorted(entries.keySet())) {
TerminologyResourceEntry e = entries.get(n);
String name = e.valueSet.getIdBase();
int i = 0;
while (names.contains(name)) {
i++;
name = e.valueSet.getIdBase()+i;
}
names.add(name);
if (e.error == null) {
b.append(name+","+n+", , "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
} else {
b.append(name+","+n+", \""+Utilities.escapeCSV(e.error)+"\", "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
}
if (json) {
gen.addFile("package",name+".json", new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(e.valueSet));
} else {
gen.addFile("package",name+".xml", new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(e.valueSet));
}
}
gen.addFile("other","valuesets.csv", b.toString().getBytes(StandardCharsets.UTF_8));
gen.finish();
}
private void produceFolder() throws IOException {
Utilities.createDirectory(output);
Utilities.clearDirectory(output);
Set<String> names = new HashSet<>();
names.add("manifest");
if (json) {
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(output, "manifest.json")), expansionParameters);
} else {
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(output, "manifest.xml")), expansionParameters);
}
StringBuilder b = new StringBuilder();
for (String n : Utilities.sorted(entries.keySet())) {
TerminologyResourceEntry e = entries.get(n);
String name = e.valueSet.getIdBase();
int i = 0;
while (names.contains(name)) {
i++;
name = e.valueSet.getIdBase()+i;
}
names.add(name);
if (e.error == null) {
b.append(name+","+n+", , "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
} else {
b.append(name+","+n+", \""+Utilities.escapeCSV(e.error)+"\", "+CommaSeparatedStringBuilder.join(";",e.sources)+"\r\n");
}
if (json) {
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(output, name+".json")), e.valueSet);
} else {
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(output, name+".xml")), e.valueSet);
}
}
TextFile.stringToFile(b.toString(), Utilities.path(output, "valuesets.csv"));
}
private void processSD(StructureDefinition sd, String packageId) {
// System.out.println("Found Structure: "+sd.getVersionedUrl());
for (ElementDefinition ed : sd.getDifferential().getElement()) {
if (ed.hasBinding() && ed.getBinding().hasValueSet()) {
// TerminologyResourceEntry e = new TerminologyResourceEntry();
processValueSet(ed.getBinding().getValueSet(), packageId+":"+sd.getVersionedUrl()+"#"+ed.getId());
}
}
if (scope != ExpansionPackageGeneratorScope.IG_ONLY && sd.getBaseDefinition() != null) {
StructureDefinition bsd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
if (!bsd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition") || scope == ExpansionPackageGeneratorScope.EVERYTHING) {
processSD(bsd, bsd.getSourcePackage().getVID());
}
}
}
private void processValueSet(String valueSet, String source) {
String url = cu.pinValueSet(valueSet);
ValueSet vs = context.fetchResource(ValueSet.class, url);
if (vs != null) {
TerminologyResourceEntry e = entries.get(vs.getVersionedUrl());
if (e == null) {
e = new TerminologyResourceEntry();
e.sources.add(source);
e.valueSet = vs;
entries.put(vs.getVersionedUrl(), e);
source = vs.getSourcePackage().getVID()+":"+vs.getVersionedUrl();
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
for (CanonicalType v : inc.getValueSet()) {
if (v.hasValue()) {
processValueSet(v.primitiveValue(), source);
}
}
}
for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
for (CanonicalType v : inc.getValueSet()) {
if (v.hasValue()) {
processValueSet(v.primitiveValue(), source);
}
}
}
} else {
e.sources.add(source);
}
} else {
System.out.println("Unable to resolve value set "+valueSet);
}
}
private NpmPackage load() throws IOException {
FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager.Builder().build();
NpmPackage npm = pcm.loadPackage(packageId);
if (context == null) {
String v = npm.fhirVersion();
NpmPackage core = pcm.loadPackage(VersionUtilities.packageForVersion(v));
NpmPackage tho = pcm.loadPackage("hl7.terminology");
System.out.println("Load FHIR from "+core.name()+"#"+core.version());
SimpleWorkerContext ctxt = new SimpleWorkerContext.SimpleWorkerContextBuilder().withAllowLoadingDuplicates(true).fromPackage(core);
TerminologyClientFactory factory = new TerminologyClientFactory(ctxt.getVersion());
ctxt.connectToTSServer(factory, "http://tx.fhir.org", ctxt.getUserAgent(), null, true);
var loader = new IgLoader(pcm, ctxt, ctxt.getVersion());
loader.loadPackage(tho, true);
loader.loadPackage(npm, true);
context = ctxt;
} else {
var loader = new IgLoader(pcm, (SimpleWorkerContext) context, context.getVersion());
loader.loadPackage(npm, true);
}
context.setExpansionParameters(expansionParameters);
cu = new ContextUtilities(context);
return npm;
}
}

View File

@ -5,6 +5,7 @@ import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.test.utils.CompareUtilities;
@ -66,6 +67,11 @@ public class TxServiceTestHelper {
if (p.hasParameter("activeOnly") && "true".equals(p.getParameterString("activeOnly"))) {
options = options.setActiveOnly(true);
}
for (ParametersParameterComponent pp : p.getParameter()) {
if (Utilities.existsInList(pp.getName(), "valueset-version", "system-version", "force-system-version", "default-system-version")) {
context.getExpansionParameters().getParameter().add(pp);
}
}
context.getExpansionParameters().clearParameters("includeAlternateCodes");
for (Parameters.ParametersParameterComponent pp : p.getParameter()) {
if ("includeAlternateCodes".equals(pp.getName())) {

View File

@ -0,0 +1,11 @@
You can use the validator to generate a package containing all the terminology resources for
an Implementation Guide. To do this, you must provide a specific parameter:
-tx-pack {package-id}
-tx-pack requires the parameter -output. All parameters:
- output {file|folder}: a named file or folder. If it's a file, it must end with .tgz or .zip
- format xml may be used to specify xml instead of json.
- scope ig|igs|all - which to include value sets etc from dependent IGs or core as well
- expansion-parameters {file} - specifies the expansion parameters to use - this can supply fixed versions for code systems and value sets

View File

@ -86,6 +86,9 @@ public class ValidatorCliTests {
@Spy
CodeGenTask codeGenTask;
@Spy
TxPackTask txPackTask;
@Spy
ScanTask scanTask = new ScanTask() {
@Override
@ -122,6 +125,7 @@ public class ValidatorCliTests {
transformTask,
versionTask,
codeGenTask,
txPackTask,
//validate is the default
validateTask
);