Merge pull request #1563 from hapifhir/2024-02-gg-misc-fixes-1
2024 02 gg misc fixes 1
This commit is contained in:
commit
1df3b9651b
|
@ -307,3 +307,5 @@ local.properties
|
|||
/org.hl7.fhir.r4b.new
|
||||
|
||||
org.hl7.fhir.r5/var/lib/.fhir/packages/
|
||||
|
||||
org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/DebugUtilities.java
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -190,7 +190,10 @@ public class R5ExtensionsLoader {
|
|||
context.cacheResourceFromPackage(vs, vs.getSourcePackage());
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
for (CanonicalType t : inc.getValueSet()) {
|
||||
loadValueSet(t.asStringValue(), context, valueSets, codeSystems);
|
||||
ValueSet vsi = context.fetchResource(ValueSet.class, t.getValue());
|
||||
if (vsi == null) {
|
||||
loadValueSet(t.asStringValue(), context, valueSets, codeSystems);
|
||||
}
|
||||
}
|
||||
if (inc.hasSystem()) {
|
||||
if (!inc.hasVersion()) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent
|
|||
import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComponent;
|
||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
@ -621,7 +622,12 @@ public class ProfilePathProcessor {
|
|||
if (firstTypeStructureDefinition.getSnapshot().getElement().isEmpty()) {
|
||||
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, firstTypeStructureDefinition.getVersionedUrl(), "Source for first element"));
|
||||
} else {
|
||||
src = firstTypeStructureDefinition.getSnapshot().getElement().get(0);
|
||||
src = firstTypeStructureDefinition.getSnapshot().getElement().get(0).copy();
|
||||
if (!src.getPath().contains(".") && firstTypeStructureDefinition.getKind() == StructureDefinitionKind.RESOURCE) {
|
||||
// we can't migrate the constraints in this case, because the sense of %resource changes when the root resource
|
||||
// is treated as an element. The validator will enforce the constraint
|
||||
src.getConstraint().clear(); //
|
||||
}
|
||||
}
|
||||
}
|
||||
template = src.copy().setPath(currentBase.getPath());
|
||||
|
|
|
@ -4605,6 +4605,11 @@ public boolean hasTarget() {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + ":" + expression + (severity == null ? "("+severity.asStringValue()+")" : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Block()
|
||||
|
@ -13093,6 +13098,10 @@ If a pattern[x] is declared on a repeating element, the pattern applies to all r
|
|||
return t;
|
||||
}
|
||||
|
||||
public boolean repeats() {
|
||||
return !Utilities.existsInList(getMax(), "0", "1");
|
||||
}
|
||||
|
||||
// end addition
|
||||
|
||||
}
|
||||
|
|
|
@ -1499,6 +1499,34 @@ public class StructureDefinition extends CanonicalResource {
|
|||
|
||||
}
|
||||
|
||||
//added from java-adornments.txt:
|
||||
|
||||
public ElementDefinition getElementByPath(String path) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
for (ElementDefinition ed : getElement()) {
|
||||
if (path.equals(ed.getPath()) || (path+"[x]").equals(ed.getPath())) {
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public ElementDefinition getElementById(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
for (ElementDefinition ed : getElement()) {
|
||||
if (id.equals(ed.getId())) {
|
||||
return ed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//end addition
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
|||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
|
||||
|
@ -539,8 +540,15 @@ public class CodeSystemRenderer extends TerminologyRenderer {
|
|||
if (first) first = false; else td.addText(", ");
|
||||
if (pcv.hasValueCoding()) {
|
||||
td.addText(pcv.getValueCoding().getCode());
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) {
|
||||
CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, pcv.getValue().primitiveValue());
|
||||
if (cr != null) {
|
||||
td.ah(cr.getWebPath(), cr.getVersionedUrl()).tx(cr.present());
|
||||
} else if (Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
|
||||
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||
} else {
|
||||
td.code(pcv.getValue().primitiveValue());
|
||||
}
|
||||
} else if ("parent".equals(pcv.getCode())) {
|
||||
td.ah("#"+cs.getId()+"-"+Utilities.nmtokenize(pcv.getValue().primitiveValue())).addText(pcv.getValue().primitiveValue());
|
||||
} else {
|
||||
|
|
|
@ -92,6 +92,12 @@ public abstract class ResourceRenderer extends DataRenderer {
|
|||
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||
boolean hasExtensions;
|
||||
hasExtensions = render(x, r);
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
inject(r, x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
|
||||
}
|
||||
|
||||
|
@ -99,12 +105,53 @@ public abstract class ResourceRenderer extends DataRenderer {
|
|||
assert r.getContext() == context;
|
||||
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
|
||||
boolean hasExtensions = render(x, r);
|
||||
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
if (r.hasNarrative()) {
|
||||
r.injectNarrative(x, hasExtensions ? NarrativeStatus.EXTENSIONS : NarrativeStatus.GENERATED);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public XhtmlNode checkNarrative(ResourceWrapper r) throws IOException, FHIRException, EOperationOutcome {
|
||||
assert r.getContext() == context;
|
||||
XhtmlNode x = r.getNarrative();
|
||||
String an = r.fhirType()+"_"+r.getId();
|
||||
if (context.isAddName()) {
|
||||
if (!hasAnchorName(x, an)) {
|
||||
injectAnchorName(x, an);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private void injectAnchorName(XhtmlNode x, String an) {
|
||||
XhtmlNode ip = x;
|
||||
while (ip.hasChildren() && "div".equals(ip.getChildNodes().get(0).getName())) {
|
||||
ip = ip.getChildNodes().get(0);
|
||||
}
|
||||
ip.addTag(0, "a").setAttribute("name", an).tx(" ");
|
||||
}
|
||||
|
||||
protected boolean hasAnchorName(XhtmlNode x, String an) {
|
||||
if ("a".equals(x.getName()) && an.equals(x.getAttribute("name"))) {
|
||||
return true;
|
||||
}
|
||||
if (x.hasChildren()) {
|
||||
for (XhtmlNode c : x.getChildNodes()) {
|
||||
if (hasAnchorName(c, an)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome;
|
||||
|
||||
public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
|
||||
|
@ -438,7 +485,11 @@ public abstract class ResourceRenderer extends DataRenderer {
|
|||
String bundleUrl = null;
|
||||
Element br = bundleElement.getNamedChild("resource", false);
|
||||
if (br.getChildValue("id") != null) {
|
||||
bundleUrl = "#" + br.fhirType() + "_" + br.getChildValue("id");
|
||||
if ("Bundle".equals(br.fhirType())) {
|
||||
bundleUrl = "#";
|
||||
} else {
|
||||
bundleUrl = "#" + br.fhirType() + "_" + br.getChildValue("id");
|
||||
}
|
||||
} else {
|
||||
bundleUrl = "#" +fullUrlToAnchor(bundleElement.getChildValue("fullUrl"));
|
||||
}
|
||||
|
|
|
@ -792,8 +792,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
|||
UnusedTracker used = new UnusedTracker();
|
||||
String ref = defPath == null ? null : defPath + anchorPrefix + element.getId();
|
||||
String sName = tail(element.getPath());
|
||||
if (element.hasSliceName())
|
||||
if (element.hasSliceName()) {
|
||||
sName = sName +":"+element.getSliceName();
|
||||
}
|
||||
used.used = true;
|
||||
if (logicalModel) {
|
||||
if (element.hasRepresentation(PropertyRepresentation.XMLATTR)) {
|
||||
|
@ -1331,10 +1332,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
|||
String ref2 = null;
|
||||
String fixedUrl = null;
|
||||
if (ed != null) {
|
||||
String p = ed.getWebPath();
|
||||
if (p != null) {
|
||||
ref = p.startsWith("http:") || context.getRules() == GenerationRules.IG_PUBLISHER ? p : Utilities.pathURL(corePath, p);
|
||||
}
|
||||
String p = ed.getWebPath();
|
||||
fixedUrl = getFixedUrl(ed);
|
||||
if (fixedUrl != null) {// if its null, we guess that it's not a profiled extension?
|
||||
if (fixedUrl.equals(url))
|
||||
|
@ -3315,6 +3313,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
|||
if (!tl.contains(tc)) {
|
||||
aliases.add(name.replace("[x]", Utilities.capitalize(tc)));
|
||||
aliases.add(name+":"+name.replace("[x]", Utilities.capitalize(tc)));
|
||||
aliases.add(name.replace("[x]", Utilities.capitalize(tc))+":"+name.replace("[x]", Utilities.capitalize(tc)));
|
||||
tl.add(tc);
|
||||
}
|
||||
}
|
||||
|
@ -3334,7 +3333,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
|
|||
list.addAll(generated);
|
||||
}
|
||||
ElementDefinition ed = stack.get(stack.size()-1);
|
||||
|
||||
// now we have all the possible names, but some of them might be inappropriate if we've
|
||||
// already generated a type slicer. On the other hand, if we've already done that, we're
|
||||
// going to steal any type specific ones off it.
|
||||
|
|
|
@ -82,6 +82,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
|
||||
private static final int MAX_DESIGNATIONS_IN_LINE = 5;
|
||||
|
||||
private static final int MAX_BATCH_VALIDATION_SIZE = 1000;
|
||||
|
||||
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
|
||||
|
||||
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
|
||||
|
@ -1431,12 +1433,23 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
}
|
||||
}
|
||||
if (!context.isNoSlowLookup() && !serverList.isEmpty()) {
|
||||
getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), serverList, null);
|
||||
for (CodingValidationRequest vr : serverList) {
|
||||
ConceptDefinitionComponent v = vr.getResult().asConceptDefinition();
|
||||
if (v != null) {
|
||||
results.put(vr.getCoding().getCode(), v);
|
||||
try {
|
||||
// todo: split this into 10k batches
|
||||
int i = 0;
|
||||
while (serverList.size() > i) {
|
||||
int len = Integer.min(serverList.size(), MAX_BATCH_VALIDATION_SIZE);
|
||||
List<CodingValidationRequest> list = serverList.subList(i, i+len);
|
||||
i += len;
|
||||
getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), list, null);
|
||||
for (CodingValidationRequest vr : list) {
|
||||
ConceptDefinitionComponent v = vr.getResult().asConceptDefinition();
|
||||
if (v != null) {
|
||||
results.put(vr.getCoding().getCode(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
|
|
@ -215,6 +215,8 @@ public class RenderingContext {
|
|||
|
||||
private Map<KnownLinkType, String> links = new HashMap<>();
|
||||
private Map<String, String> namedLinks = new HashMap<>();
|
||||
private boolean addName = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context - access to all related resources that might be needed
|
||||
|
@ -728,5 +730,14 @@ public class RenderingContext {
|
|||
this.fixedFormat = fixedFormat;
|
||||
}
|
||||
|
||||
public boolean isAddName() {
|
||||
return addName;
|
||||
}
|
||||
|
||||
public RenderingContext setAddName(boolean addName) {
|
||||
this.addName = addName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
|
@ -10,6 +14,8 @@ import org.hl7.fhir.r5.model.ConceptMap;
|
|||
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
|
||||
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ConceptMapElementSorter;
|
||||
import org.hl7.fhir.r5.model.Identifier;
|
||||
import org.hl7.fhir.r5.model.Meta;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
|
@ -17,6 +23,40 @@ import org.hl7.fhir.r5.model.ValueSet;
|
|||
|
||||
public class ConceptMapUtilities {
|
||||
|
||||
public static class TranslatedCode {
|
||||
private String code;
|
||||
private ConceptMapRelationship relationship;
|
||||
public TranslatedCode(String code, ConceptMapRelationship relationship) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.relationship = relationship;
|
||||
}
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
public ConceptMapRelationship getRelationship() {
|
||||
return relationship;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConceptMapElementSorter implements Comparator<SourceElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(SourceElementComponent o1, SourceElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConceptMapTargetElementSorter implements Comparator<TargetElementComponent> {
|
||||
|
||||
@Override
|
||||
public int compare(TargetElementComponent o1, TargetElementComponent o2) {
|
||||
return o1.getCode().compareTo(o2.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
public static boolean hasOID(ConceptMap cm) {
|
||||
return getOID(cm) != null;
|
||||
}
|
||||
|
@ -85,4 +125,167 @@ public class ConceptMapUtilities {
|
|||
return cm;
|
||||
}
|
||||
|
||||
public static ConceptMap invert(ConceptMap src, String id, String url, String name, boolean collate) {
|
||||
ConceptMap dst = src.copy();
|
||||
dst.setId(id);
|
||||
dst.setUrl(url);
|
||||
dst.setName(name);
|
||||
dst.getGroup().clear();
|
||||
dst.setSourceScope(src.getTargetScope());
|
||||
dst.setTargetScope(src.getSourceScope());
|
||||
for (ConceptMapGroupComponent gs : src.getGroup()) {
|
||||
ConceptMapGroupComponent gd = dst.addGroup();
|
||||
gd.setTargetElement(gs.getSourceElement());
|
||||
gd.setSourceElement(gs.getTargetElement());
|
||||
Map<String, SourceElementComponent> dstMap = new HashMap<>();
|
||||
for (SourceElementComponent es : gs.getElement()) {
|
||||
for (TargetElementComponent ts : es.getTarget()) {
|
||||
SourceElementComponent ed = collate ? dstMap.get(ts.getCode()) : null;
|
||||
if (ed == null) {
|
||||
ed = gd.addElement();
|
||||
ed.setCodeElement(ts.getCodeElement());
|
||||
if (collate) {
|
||||
dstMap.put(ed.getCode(), ed);
|
||||
}
|
||||
}
|
||||
TargetElementComponent td = ed.addTarget();
|
||||
td.setCode(es.getCode());
|
||||
td.setComment(ts.getComment());
|
||||
td.setRelationship(invertRelationship(ts.getRelationship()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static ConceptMapRelationship invertRelationship(ConceptMapRelationship relationship) {
|
||||
if (relationship == null) {
|
||||
return null;
|
||||
}
|
||||
switch (relationship) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.EQUIVALENT;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return ConceptMapRelationship.NULL;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConceptMap collapse(String id, String url, boolean cumulative, ConceptMap src, ConceptMap... sequence) {
|
||||
ConceptMap res = src.copy();
|
||||
res.setId(id);
|
||||
res.setUrl(url);
|
||||
|
||||
for (ConceptMap cm : sequence) {
|
||||
if (res.hasTargetScope() && src.hasTargetScope()) {
|
||||
if (!cm.getSourceScope().equals(cm.getTargetScope())) {
|
||||
throw new Error("Mismatch between seqeuntial concept maps: ");
|
||||
} else {
|
||||
res.setTargetScope(cm.getTargetScope());
|
||||
}
|
||||
} else {
|
||||
res.setTargetScope(null);
|
||||
}
|
||||
}
|
||||
|
||||
for (ConceptMapGroupComponent gd : res.getGroup()) {
|
||||
for (ConceptMap cm : sequence) {
|
||||
for (ConceptMapGroupComponent gt : cm.getGroup()) {
|
||||
if (gt.getSource().equals(gd.getTarget())) {
|
||||
gd.setTarget(gt.getTarget());
|
||||
|
||||
List<SourceElementComponent> processed = new ArrayList<ConceptMap.SourceElementComponent>();
|
||||
for (SourceElementComponent ed : gd.getElement()) {
|
||||
List<TargetElementComponent> list = new ArrayList<>();
|
||||
list.addAll(ed.getTarget());
|
||||
ed.getTarget().clear();
|
||||
for (TargetElementComponent ts : list) {
|
||||
for (SourceElementComponent et : gt.getElement()) {
|
||||
if (et.getCode().equals(ed.getCode())) {
|
||||
processed.add(et);
|
||||
for (TargetElementComponent tt : et.getTarget()) {
|
||||
ed.addTarget().setCode(tt.getCode()).setRelationship(combineRelationships(ts.getRelationship(), tt.getRelationship()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ed.getTarget().isEmpty()) {
|
||||
if (cumulative) {
|
||||
ed.getTarget().addAll(list);
|
||||
} else {
|
||||
ed.setNoMap(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cumulative) {
|
||||
for (SourceElementComponent et : gt.getElement()) {
|
||||
if (!processed.contains(et)) {
|
||||
gd.addElement(et.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(gt.getElement(), new ConceptMapElementSorter());
|
||||
for (SourceElementComponent e: gt.getElement()) {
|
||||
Collections.sort(e.getTarget(), new ConceptMapTargetElementSorter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ConceptMapRelationship combineRelationships(ConceptMapRelationship rel1, ConceptMapRelationship rel2) {
|
||||
switch (rel1) {
|
||||
case EQUIVALENT:
|
||||
return rel2;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return rel2;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
switch (rel2) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISBROADERTHANTARGET;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
}
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
switch (rel2) {
|
||||
case EQUIVALENT:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
case NOTRELATEDTO:
|
||||
return ConceptMapRelationship.NOTRELATEDTO;
|
||||
case NULL:
|
||||
return null;
|
||||
case RELATEDTO:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISBROADERTHANTARGET:
|
||||
return ConceptMapRelationship.RELATEDTO;
|
||||
case SOURCEISNARROWERTHANTARGET:
|
||||
return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public class XVerExtensionManager {
|
|||
|
||||
public static final String XVER_EXT_MARKER = "XVER_EXT_MARKER";
|
||||
|
||||
public static final String XVER_VER_MARKER = "XVER_VER_MARKER";
|
||||
|
||||
private Map<String, JsonObject> lists = new HashMap<>();
|
||||
private IWorkerContext context;
|
||||
|
||||
|
@ -92,6 +94,7 @@ public class XVerExtensionManager {
|
|||
|
||||
StructureDefinition sd = new StructureDefinition();
|
||||
sd.setUserData(XVER_EXT_MARKER, "true");
|
||||
sd.setUserData(XVER_VER_MARKER, verSource);
|
||||
if (context.getResourceNamesAsSet().contains(r)) {
|
||||
sd.setWebPath(Utilities.pathURL(context.getSpecUrl(), r.toLowerCase()+"-definitions.html#"+e));
|
||||
} else {
|
||||
|
|
|
@ -161,4 +161,14 @@ public class CommaSeparatedStringBuilder {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static String joinWrapped(String sep, String leftWrap, String rightWrap, Collection<String> list) {
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(sep);
|
||||
for (String s : list) {
|
||||
if (s != null) {
|
||||
b.append(leftWrap+s+rightWrap);
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
}
|
|
@ -729,5 +729,15 @@ public class VersionUtilities {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean includedInRange(String startVer, String stopVer, String ver) {
|
||||
if (ver.equals(startVer)) {
|
||||
return true;
|
||||
}
|
||||
if (ver.equals(stopVer)) {
|
||||
return true;
|
||||
}
|
||||
return startVer.compareTo(ver) < 0 && stopVer.compareTo(ver) > 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -551,6 +551,8 @@ public class I18nConstants {
|
|||
public static final String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URI_WS = "Type_Specific_Checks_DT_URI_WS";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE = "Type_Specific_Checks_DT_URL_Resolve";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE = "Type_Specific_Checks_DT_XHTML_Resolve";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE_IMG = "Type_Specific_Checks_DT_XHTML_Resolve_Img";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE = "TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE";
|
||||
|
@ -1048,6 +1050,13 @@ public class I18nConstants {
|
|||
public static final String SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH = "SD_CONTEXT_SHOULD_NOT_BE_FHIRPATH";
|
||||
public static final String TX_GENERAL_CC_ERROR_MESSAGE = "TX_GENERAL_CC_ERROR_MESSAGE";
|
||||
public static final String FHIRPATH_UNKNOWN_EXTENSION = "FHIRPATH_UNKNOWN_EXTENSION";
|
||||
public static final String TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES = "TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES";
|
||||
public static final String CONTAINED_ORPHAN_DOM3 = "CONTAINED_ORPHAN_DOM3";
|
||||
public static final String VALUESET_INCLUDE_CS_NOT_CS = "VALUESET_INCLUDE_CS_NOT_CS";
|
||||
public static final String VALUESET_INCLUDE_CS_NOT_FOUND = "VALUESET_INCLUDE_CS_NOT_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CSVER_NOT_FOUND = "VALUESET_INCLUDE_CSVER_NOT_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CS_MULTI_FOUND = "VALUESET_INCLUDE_CS_MULTI_FOUND";
|
||||
public static final String VALUESET_INCLUDE_CSVER_MULTI_FOUND = "VALUESET_INCLUDE_CSVER_MULTI_FOUND";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -976,5 +976,27 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
|
|||
}
|
||||
return btn;
|
||||
}
|
||||
|
||||
|
||||
public XhtmlNode head() {
|
||||
return addTag("head");
|
||||
}
|
||||
|
||||
public XhtmlNode body() {
|
||||
return addTag("body");
|
||||
}
|
||||
public XhtmlNode title(String title) {
|
||||
return addTag("title").tx(title);
|
||||
}
|
||||
|
||||
public XhtmlNode link(String rel, String href) {
|
||||
return addTag("link").attribute("rel", rel).attribute("href", href);
|
||||
}
|
||||
|
||||
|
||||
public void wbr() {
|
||||
addTag("wbr");
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -917,10 +917,10 @@ CONCEPTMAP_GROUP_TARGET_MISSING = No Target Code System, so the target codes can
|
|||
CONCEPTMAP_GROUP_TARGET_UNKNOWN = The Target Code System {0} is not fully defined and populated, and no targetScope is specified, so the target code checking will not be performed
|
||||
CONCEPTMAP_GROUP_SOURCE_CODE_INVALID = The source code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_CODE_INVALID_VS = The source code ''{0}'' is not valid in the value set {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' is not valid. Possible codes {1}
|
||||
CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' for the code ''{2}'' is not valid. Possible displays: {1}
|
||||
CONCEPTMAP_GROUP_TARGET_CODE_INVALID = The target code ''{0}'' is not valid in the code system {1}
|
||||
CONCEPTMAP_GROUP_TARGET_CODE_INVALID_VS = The target code ''{0}'' is not valid in the value set {1}
|
||||
CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = The target display ''{0}'' is not valid. Possible displays {1}
|
||||
CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID = The target display ''{0}'' for the code ''{2}'' is not valid. Possible displays: {1}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_INVALID = The property code ''{0}'' is not known
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_MISMATCH = The type of this property should be {1} not {0}
|
||||
CONCEPTMAP_GROUP_TARGET_PROPERTY_TYPE_NO_SYSTEM = Since no system has been provided, a plain code cannot be used
|
||||
|
@ -1059,7 +1059,7 @@ CONCEPTMAP_VS_TOO_MANY_CODES = The concept map has too many codes to validate ({
|
|||
CONCEPTMAP_VS_CONCEPT_CODE_UNKNOWN_SYSTEM = The code ''{1}'' comes from the system {0} which could not be found, so it''s not known whether it''s valid in the value set ''{2}''
|
||||
CONCEPTMAP_VS_INVALID_CONCEPT_CODE = The code ''{1}'' in the system {0} is not valid in the value set ''{2}''
|
||||
CONCEPTMAP_VS_INVALID_CONCEPT_CODE_VER = The code ''{2}'' in the system {0} version {1} is not valid in the value set ''{3}''
|
||||
VALUESET_INC_TOO_MANY_CODES = The value set include has too many codes to validate ({0})
|
||||
VALUESET_INC_TOO_MANY_CODES = The value set include has too many codes to validate ({0}), so each individual code has not been checked
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH = The {1} resource did not match any of the allowed profiles (Type {2}: {3})
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_MULTIPLE_MATCHES = The {1} resource matched more than one of the allowed profiles ({3})
|
||||
BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES_NO_MATCH_REASON = The {1} resource did not math the profile {2} because: {3}
|
||||
|
@ -1106,3 +1106,12 @@ TX_GENERAL_CC_ERROR_MESSAGE = No valid coding was found for the value set ''{0}'
|
|||
Validation_VAL_Profile_Minimum_SLICE_one = Slice ''{3}'': a matching slice is required, but not found (from {1}). Note that other slices are allowed in addition to this required slice
|
||||
Validation_VAL_Profile_Minimum_SLICE_other = Slice ''{3}'': minimum required = {0}, but only found {7} (from {1})
|
||||
FHIRPATH_UNKNOWN_EXTENSION = Reference to an unknown extension - double check that the URL ''{0}'' is correct
|
||||
Type_Specific_Checks_DT_XHTML_Resolve = Hyperlink ''{0}'' at ''{1}'' for ''{2}''' does not resolve
|
||||
Type_Specific_Checks_DT_XHTML_Resolve_Img = Image source ''{0}'' at ''{1}'' does not resolve
|
||||
TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES = Hyperlink ''{0}'' at ''{1}'' for ''{2}''' resolves to multiple targets
|
||||
CONTAINED_ORPHAN_DOM3 = The contained resource ''{0}'' is not referenced to from elsewhere in the containing resource nor does it refer to the containing resource (dom-3)
|
||||
VALUESET_INCLUDE_CS_NOT_CS = The include system ''{0}'' is a reference to a contained resource, but the contained resource with that id is not a CodeSystem, it's a {1}
|
||||
VALUESET_INCLUDE_CS_NOT_FOUND = No matching contained code system found for system ''{0}''
|
||||
VALUESET_INCLUDE_CSVER_NOT_FOUND = No matching contained code system found for system ''{0}'' version ''{1}''
|
||||
VALUESET_INCLUDE_CS_MULTI_FOUND = Multiple matching contained code systems found for system ''{0}''
|
||||
VALUESET_INCLUDE_CSVER_MULTI_FOUND = Multiple matching contained code systems found for system ''{0}'' version ''{1}''
|
||||
|
|
|
@ -85,7 +85,7 @@ import org.hl7.fhir.validation.instance.utils.NodeStack;
|
|||
|
||||
public class BaseValidator implements IValidationContextResourceLoader {
|
||||
|
||||
public class BooleanHolder {
|
||||
public static class BooleanHolder {
|
||||
private boolean value = true;
|
||||
|
||||
public BooleanHolder() {
|
||||
|
|
|
@ -494,18 +494,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
Element e = new ObjectConverter(context).convert((Resource) item);
|
||||
setParents(e);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null,
|
||||
mode);
|
||||
mode, false);
|
||||
} catch (IOException e1) {
|
||||
throw new FHIRException(e1);
|
||||
}
|
||||
} else if (item instanceof Element) {
|
||||
Element e = (Element) item;
|
||||
if (e.getSpecial() == SpecialElement.CONTAINED) {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, ctxt.getRootResource(), ctxt.getGroupingResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false);
|
||||
} else if (e.getSpecial() != null) {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e, e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false);
|
||||
} else {
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode);
|
||||
self.validateResource(new ValidationContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage), null, mode, false);
|
||||
}
|
||||
} else
|
||||
throw new NotImplementedException(context.formatMessage(I18nConstants.NOT_DONE_YET_VALIDATORHOSTSERVICESCONFORMSTOPROFILE_WHEN_ITEM_IS_NOT_AN_ELEMENT));
|
||||
|
@ -1002,7 +1002,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
long t = System.nanoTime();
|
||||
NodeStack stack = new NodeStack(context, path, element, validationLanguage);
|
||||
if (profiles == null || profiles.isEmpty()) {
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition));
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, null, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.BaseDefinition), false);
|
||||
} else {
|
||||
int i = 0;
|
||||
while (i < profiles.size()) {
|
||||
|
@ -1020,7 +1020,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
i++;
|
||||
}
|
||||
for (StructureDefinition defn : profiles) {
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile));
|
||||
validateResource(new ValidationContext(appContext, element), errors, element, element, defn, resourceIdRule, stack.resetIds(), null, new ValidationMode(ValidationReason.Validation, ProfileSource.ConfigProfile), false);
|
||||
}
|
||||
}
|
||||
if (hintAboutNonMustSupport) {
|
||||
|
@ -2319,10 +2319,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (!ok) {
|
||||
if (definition.hasUserData(XVerExtensionManager.XVER_EXT_MARKER)) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false,
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString());
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG_XVER : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG_XVER, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER));
|
||||
} else {
|
||||
rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, container.line(), container.col(), stack.getLiteralPath(), false,
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString());
|
||||
modifier ? I18nConstants.EXTENSION_EXTM_CONTEXT_WRONG : I18nConstants.EXTENSION_EXTP_CONTEXT_WRONG, extUrl, contexts.toString(), plist.toString(), definition.getUserString(XVerExtensionManager.XVER_VER_MARKER));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
|
@ -2399,7 +2399,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else if (sd.getType().equals(resource.fhirType())) {
|
||||
List<ValidationMessage> valerrors = new ArrayList<ValidationMessage>();
|
||||
ValidationMode mode = new ValidationMode(ValidationReason.Expression, ProfileSource.FromExpression);
|
||||
validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode);
|
||||
validateResource(new ValidationContext(appContext, resource), valerrors, resource, resource, sd, IdStatus.OPTIONAL, new NodeStack(context, null, resource, validationLanguage), null, mode, false);
|
||||
boolean ok = true;
|
||||
List<ValidationMessage> record = new ArrayList<>();
|
||||
for (ValidationMessage v : valerrors) {
|
||||
|
@ -2951,6 +2951,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
ok = checkInnerNames(errors, e, path, xhtml.getChildNodes(), false) && ok;
|
||||
ok = checkUrls(errors, e, path, xhtml.getChildNodes()) && ok;
|
||||
ok = checkIdRefs(errors, e, path, xhtml, resource) && ok;
|
||||
if (true) {
|
||||
ok = checkReferences(valContext, errors, e, path, "div", xhtml, resource) && ok;
|
||||
}
|
||||
if (true) {
|
||||
ok = checkImageSources(valContext, errors, e, path, "div", xhtml, resource) && ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3066,6 +3072,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
boolean ok = true;
|
||||
// now, do we check the URI target?
|
||||
if (fetcher != null && !type.equals("uuid")) {
|
||||
if (url.startsWith("#")) {
|
||||
valContext.getInternalRefs().add(url.substring(1));
|
||||
}
|
||||
boolean found;
|
||||
try {
|
||||
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) /* || (url.startsWith("http://hl7.org/fhir/tools")) */ ||
|
||||
|
@ -3319,6 +3328,99 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkReferences(ValidationContext valContext, List<ValidationMessage> errors, Element e, String path, String xpath, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element & "a".equals(node.getName()) && node.getAttribute("href") != null) {
|
||||
String href = node.getAttribute("href");
|
||||
if (!Utilities.noString(href) && href.startsWith("#") && !href.equals("#")) {
|
||||
String ref = href.substring(1);
|
||||
valContext.getInternalRefs().add(ref);
|
||||
int count = countTargetMatches(resource, ref, true);
|
||||
if (count == 0) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE, href, xpath, node.allText());
|
||||
} else if (count > 1) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, href, xpath, node.allText());
|
||||
}
|
||||
} else {
|
||||
// we can't validate at this point. Come back and revisit this some time in the future
|
||||
}
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
checkReferences(valContext, errors, e, path, xpath+"/"+child.getName(), child, resource);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
protected int countTargetMatches(Element element, String fragment, boolean checkBundle) {
|
||||
int count = 0;
|
||||
if (fragment.equals(element.getIdBase())) {
|
||||
count++;
|
||||
}
|
||||
if (element.getXhtml() != null) {
|
||||
count = count + countTargetMatches(element.getXhtml(), fragment);
|
||||
}
|
||||
if (element.hasChildren()) {
|
||||
for (Element child : element.getChildren()) {
|
||||
count = count + countTargetMatches(child, fragment, false);
|
||||
}
|
||||
}
|
||||
if (count == 0 && checkBundle) {
|
||||
Element e = element.getParentForValidator();
|
||||
while (e != null) {
|
||||
if (e.fhirType().equals("Bundle")) {
|
||||
return countTargetMatches(e, fragment, false);
|
||||
}
|
||||
e = e.getParentForValidator();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int countTargetMatches(XhtmlNode node, String fragment) {
|
||||
int count = 0;
|
||||
if (fragment.equals(node.getAttribute("id"))) {
|
||||
count++;
|
||||
}
|
||||
if ("a".equals(node.getName()) && fragment.equals(node.getAttribute("name"))) {
|
||||
count++;
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
count = count + countTargetMatches(child, fragment);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkImageSources(ValidationContext valContext, List<ValidationMessage> errors, Element e, String path, String xpath, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element & "img".equals(node.getName()) && node.getAttribute("src") != null) {
|
||||
String src = node.getAttribute("src");
|
||||
if (src.startsWith("#")) {
|
||||
String ref = src.substring(1);
|
||||
valContext.getInternalRefs().add(ref);
|
||||
int count = countFragmentMatches(resource, ref);
|
||||
if (count == 0) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_RESOLVE_IMG, src, xpath);
|
||||
} else if (count > 1) {
|
||||
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_XHTML_MULTIPLE_MATCHES, src, xpath);
|
||||
}
|
||||
} else {
|
||||
// we can't validate at this point. Come back and revisit this some time in the future
|
||||
}
|
||||
}
|
||||
if (node.hasChildren()) {
|
||||
for (XhtmlNode child : node.getChildNodes()) {
|
||||
checkImageSources(valContext, errors, e, path, path+"/"+child.getName(), child, resource);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkIdRefs(List<ValidationMessage> errors, Element e, String path, XhtmlNode node, Element resource) {
|
||||
boolean ok = true;
|
||||
if (node.getNodeType() == NodeType.Element && node.getAttribute("idref") != null) {
|
||||
|
@ -3764,6 +3866,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
ok = bh.ok() && ok;
|
||||
String refType;
|
||||
if (ref.startsWith("#")) {
|
||||
valContext.getInternalRefs().add(ref.substring(1));
|
||||
refType = "contained";
|
||||
} else {
|
||||
if (we == null) {
|
||||
|
@ -3889,7 +3992,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
for (StructureDefinition pr : profiles) {
|
||||
List<ValidationMessage> profileErrors = new ArrayList<ValidationMessage>();
|
||||
validateResource(we.valContext(valContext, pr), profileErrors, we.getResource(), we.getFocus(), pr,
|
||||
IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice));
|
||||
IdStatus.OPTIONAL, we.getStack().resetIds(), pct, vmode.withReason(ValidationReason.MatchingSlice), true);
|
||||
if (!hasErrors(profileErrors)) {
|
||||
goodCount++;
|
||||
goodProfiles.put(pr, profileErrors);
|
||||
|
@ -5829,7 +5932,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
|
||||
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_EXPL, special == null ? "??" : special.toHuman(), resourceName, typeForResource.getProfile().get(0).asStringValue())) {
|
||||
trackUsage(profile, valContext, element);
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
@ -5841,7 +5944,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
trackUsage(profile, valContext, element);
|
||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(),
|
||||
profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE, special == null ? "??" : special.toHuman(), resourceName)) {
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode) && ok;
|
||||
ok = validateResource(hc, errors, resource, element, profile, idstatus, stack, pct, mode, false) && ok;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
@ -5862,7 +5965,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
trackUsage(profile, valContext, element);
|
||||
List<ValidationMessage> perrors = new ArrayList<>();
|
||||
errorsList.add(perrors);
|
||||
if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode)) {
|
||||
if (validateResource(hc, perrors, resource, element, profile, idstatus, stack, pct, mode, false)) {
|
||||
bm.append(u.asStringValue());
|
||||
matched++;
|
||||
}
|
||||
|
@ -6942,6 +7045,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if (debug) {
|
||||
System.out.println("inv "+inv.getKey()+" on "+path+" in "+resource.fhirType()+" {{ "+inv.getExpression()+" }}"+time());
|
||||
}
|
||||
// we don't allow dom-3 to execute - it takes too long (and is wrong).
|
||||
// instead, we enforce it in code
|
||||
if ("dom-3".equals(inv.getKey())) {
|
||||
return true;
|
||||
}
|
||||
ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache");
|
||||
if (n == null) {
|
||||
long t = System.nanoTime();
|
||||
|
@ -7014,7 +7122,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
* The actual base entry point for internal use (re-entrant)
|
||||
*/
|
||||
private boolean validateResource(ValidationContext valContext, List<ValidationMessage> errors, Element resource,
|
||||
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode) throws FHIRException {
|
||||
Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack, PercentageTracker pct, ValidationMode mode, boolean forReference) throws FHIRException {
|
||||
|
||||
boolean ok = true;
|
||||
// check here if we call validation policy here, and then change it to the new interface
|
||||
|
@ -7070,6 +7178,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else {
|
||||
ok = false;
|
||||
}
|
||||
if (!forReference) {
|
||||
// last step: check that all contained resources are referenced or reference #
|
||||
ok = checkContainedReferences(valContext, errors, element, stack) && ok;
|
||||
}
|
||||
}
|
||||
if (testMode && ok == hasErrors(errors)) {
|
||||
throw new Error("ok is wrong. ok = "+ok+", errors = "+errorIds(stack.getLiteralPath(), ok, errors));
|
||||
|
@ -7077,6 +7189,58 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkContainedReferences(ValidationContext valContext, List<ValidationMessage> errors, Element element, NodeStack stack) {
|
||||
boolean ok = true;
|
||||
Set<String> baseRefs = (Set<String>) element.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
List<Element> containedList = element.getChildrenByName("contained");
|
||||
if (!containedList.isEmpty()) {
|
||||
boolean allDone = true;
|
||||
for (Element contained : containedList) {
|
||||
allDone = allDone && contained.hasUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
}
|
||||
if (allDone) {
|
||||
// We collected all the internal references in sets on the resource and the contained resources
|
||||
int i = 0;
|
||||
for (Element contained : containedList) {
|
||||
ok = checkContainedReferences(errors, stack, ok, baseRefs, containedList, i, contained);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkContainedReferences(List<ValidationMessage> errors, NodeStack stack, boolean ok,
|
||||
Set<String> baseRefs, List<Element> containedList, int i, Element contained) {
|
||||
NodeStack n = stack.push(contained, i, null, null);
|
||||
boolean found = isReferencedFromBase(contained, baseRefs, containedList, new ArrayList<>());
|
||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, n, found, I18nConstants.CONTAINED_ORPHAN_DOM3, contained.getIdBase()) && ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean isReferencedFromBase(Element contained, Set<String> baseRefs, List<Element> containedList, List<Element> ignoreList) {
|
||||
String id = contained.getIdBase();
|
||||
if (baseRefs.contains(id)) {
|
||||
return true;
|
||||
}
|
||||
Set<String> irefs = (Set<String>) contained.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
if (irefs.contains("")) {
|
||||
return true;
|
||||
}
|
||||
for (Element c : containedList) {
|
||||
if (c != contained && !ignoreList.contains(c)) { // ignore list is to prevent getting into an unterminated loop
|
||||
Set<String> refs = (Set<String>) c.getUserData(ValidationContext.INTERNAL_REFERENCES_NAME);
|
||||
List<Element> ignoreList2 = new ArrayList<Element>();
|
||||
ignoreList.addAll(ignoreList);
|
||||
ignoreList.add(c);
|
||||
if (refs != null && refs.contains(id) && isReferencedFromBase(c, baseRefs, containedList, ignoreList2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkResourceName(StructureDefinition defn, String resourceName, FhirFormat format) {
|
||||
if (resourceName.equals(defn.getType())) {
|
||||
return true;
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
|
|||
import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
|
||||
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
@ -277,7 +278,7 @@ public class ConceptMapValidator extends BaseValidator {
|
|||
if (warningOrError(ctxt.source.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_SOURCE_CODE_INVALID, c, ctxt.source.cs.getVersionedUrl())) {
|
||||
Element display = src.getNamedChild("display", false);
|
||||
if (display != null) {
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.source.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID, display.getValue(), CodeSystemUtilities.getDisplays(ctxt.source.cs, cd));
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.source.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID, display.getValue(), CommaSeparatedStringBuilder.joinWrapped(", ", "'", "'", CodeSystemUtilities.getDisplays(ctxt.source.cs, cd)), ctxt.source.cs.getVersionedUrl()+"#"+cd.getCode());
|
||||
}
|
||||
if (ctxt.hasSourceVS() && ctxt.source != null) {
|
||||
ValidationResult vr = context.validateCode(options.withCheckValueSetOnly().withNoServer(), ctxt.source.url, ctxt.source.version, c, null, ctxt.sourceScope.vs);
|
||||
|
@ -314,7 +315,7 @@ public class ConceptMapValidator extends BaseValidator {
|
|||
if (warningOrError(ctxt.target.cs.getContent() == CodeSystemContentMode.COMPLETE, errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), cd != null, I18nConstants.CONCEPTMAP_GROUP_TARGET_CODE_INVALID, c, ctxt.target.cs.getVersionedUrl())) {
|
||||
Element display = tgt.getNamedChild("display", false);
|
||||
if (display != null) {
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.target.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID, display.getValue(), CodeSystemUtilities.getDisplays(ctxt.target.cs, cd));
|
||||
warning(errors, "2023-03-05", IssueType.REQUIRED, code.line(), code.col(), cstack.getLiteralPath(), CodeSystemUtilities.checkDisplay(ctxt.target.cs, cd, display.getValue()), I18nConstants.CONCEPTMAP_GROUP_TARGET_DISPLAY_INVALID, display.getValue(), CommaSeparatedStringBuilder.joinWrapped(", ", "'", "'", CodeSystemUtilities.getDisplays(ctxt.target.cs, cd)), ctxt.target.cs.getVersionedUrl()+"#"+cd.getCode());
|
||||
}
|
||||
if (ctxt.hasTargetVS() && ctxt.target != null) {
|
||||
ValidationResult vr = context.validateCode(options.withCheckValueSetOnly().withNoServer(), ctxt.target.url, ctxt.target.version, c, null, ctxt.targetScope.vs);
|
||||
|
|
|
@ -60,7 +60,7 @@ public class ObservationValidator extends BaseValidator {
|
|||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "LOINC", codes, pct, mode) && ok;
|
||||
} else if (hasLoincCode(code, codes, "39156-5", "39156-5", "59574-4", "89270-3")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "LOINC", codes, pct, mode) && ok;
|
||||
} else if (hasLoincCode(code, codes, "85354-9", "96607-7", "35094-2", "8459-0", "85354-9", "76534-7", "96607-7", "55284-4", "8480-6")) {
|
||||
} else if (hasLoincCode(code, codes, "85354-9", "35094-2", "8459-0", "85354-9", "76534-7", "55284-4", "8480-6")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "LOINC", codes, pct, mode) && ok;
|
||||
|
||||
} else if (hasSctCode(code, codes, "46680005")) {
|
||||
|
@ -81,7 +81,7 @@ public class ObservationValidator extends BaseValidator {
|
|||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bodyweight", "Body weight", "SNOMED CT", codes, pct, mode) && ok;
|
||||
} else if (hasSctCode(code, codes, "60621009")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bmi", "Body mass index", "SNOMED CT", codes, pct, mode) && ok;
|
||||
} else if (hasSctCode(code, codes, "75367002", "251076008", "6797001", "163033001", "123820005", "163035008", "723232008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) {
|
||||
} else if (hasSctCode(code, codes, "75367002", "251076008", "163033001", "163035008", "386534000", "386536003", "271649006", "271649006", "271650006", "407556006", "407554009", "716579001", "399304008")) {
|
||||
ok = checkObservationAgainstProfile(valContext,errors, element, stack, "http://hl7.org/fhir/StructureDefinition/bp", "Blood pressure systolic and diastolic", "SNOMED CT", codes, pct, mode) && ok;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class ValueSetValidator extends BaseValidator {
|
|||
List<Element> composes = vs.getChildrenByName("compose");
|
||||
int cc = 0;
|
||||
for (Element compose : composes) {
|
||||
ok = validateValueSetCompose(errors, compose, stack.push(compose, composes.size() > 1 ? cc : -1, null, null), vs.getNamedChildValue("url", false), "retired".equals(vs.getNamedChildValue("url", false))) & ok;
|
||||
ok = validateValueSetCompose(errors, compose, stack.push(compose, composes.size() > 1 ? cc : -1, null, null), vs.getNamedChildValue("url", false), "retired".equals(vs.getNamedChildValue("url", false)), vs) & ok;
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
|
@ -98,24 +98,24 @@ public class ValueSetValidator extends BaseValidator {
|
|||
}
|
||||
|
||||
|
||||
private boolean validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack, String vsid, boolean retired) {
|
||||
private boolean validateValueSetCompose(List<ValidationMessage> errors, Element compose, NodeStack stack, String vsid, boolean retired, Element vsSrc) {
|
||||
boolean ok = true;
|
||||
List<Element> includes = compose.getChildrenByName("include");
|
||||
int ci = 0;
|
||||
for (Element include : includes) {
|
||||
ok = validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid, retired) && ok;
|
||||
ok = validateValueSetInclude(errors, include, stack.push(include, ci, null, null), vsid, retired, vsSrc) && ok;
|
||||
ci++;
|
||||
}
|
||||
List<Element> excludes = compose.getChildrenByName("exclude");
|
||||
int ce = 0;
|
||||
for (Element exclude : excludes) {
|
||||
ok = validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid, retired) && ok;
|
||||
ok = validateValueSetInclude(errors, exclude, stack.push(exclude, ce, null, null), vsid, retired, vsSrc) && ok;
|
||||
ce++;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack, String vsid, boolean retired) {
|
||||
private boolean validateValueSetInclude(List<ValidationMessage> errors, Element include, NodeStack stack, String vsid, boolean retired, Element vsSrc) {
|
||||
boolean ok = true;
|
||||
String system = include.getChildValue("system");
|
||||
String version = include.getChildValue("version");
|
||||
|
@ -141,6 +141,25 @@ public class ValueSetValidator extends BaseValidator {
|
|||
if (valuesets.size() > 1) {
|
||||
warning(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, stack.getLiteralPath(), false, I18nConstants.VALUESET_IMPORT_UNION_INTERSECTION);
|
||||
}
|
||||
if (system != null && system.startsWith("#")) {
|
||||
List<Element> cs = new ArrayList<>();
|
||||
for (Element contained : vsSrc.getChildrenByName("contained")) {
|
||||
if (("#"+contained.getIdBase()).equals(system)) {
|
||||
if (rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), "CodeSystem".equals(contained.fhirType()), I18nConstants.VALUESET_INCLUDE_CS_NOT_CS, system, contained.fhirType())) {
|
||||
if (version == null || version.equals(contained.getChildValue("version"))) {
|
||||
cs.add(contained);
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cs.isEmpty()) {
|
||||
ok = rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), false, version == null ? I18nConstants.VALUESET_INCLUDE_CS_NOT_FOUND : I18nConstants.VALUESET_INCLUDE_CSVER_NOT_FOUND, system, version) && ok;
|
||||
} else {
|
||||
ok = rule(errors, "2024-02-10", IssueType.INVALID, stack.getLiteralPath(), cs.size() == 1, version == null ? I18nConstants.VALUESET_INCLUDE_CS_MULTI_FOUND : I18nConstants.VALUESET_INCLUDE_CSVER_MULTI_FOUND, system, version) && ok;
|
||||
}
|
||||
}
|
||||
List<Element> concepts = include.getChildrenByName("concept");
|
||||
List<Element> filters = include.getChildrenByName("filter");
|
||||
|
||||
|
|
|
@ -9,10 +9,16 @@ public class FHIRPathExpressionFixer {
|
|||
// this is a hack work around for past publication of wrong FHIRPath expressions
|
||||
|
||||
boolean r5 = VersionUtilities.isR5Ver(version);
|
||||
// if (r5) {
|
||||
// return expr;
|
||||
// }
|
||||
boolean r4 = VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version);
|
||||
|
||||
// see https://chat.fhir.org/#narrow/stream/196008-ig-publishing-requirements/topic/Operation.20Definition.20Parameters.20table
|
||||
if (r5 && "opd-3".equals(key)) {
|
||||
return "targetProfile.exists() implies (type = 'Reference' or type = 'canonical' or type.memberOf('http://hl7.org/fhir/ValueSet/all-resource-types'))";
|
||||
}
|
||||
if (r4 && "opd-3".equals(key)) {
|
||||
return "targetProfile.exists() implies (type = 'Reference' or type = 'canonical' or type.memberOf('http://hl7.org/fhir/ValueSet/resource-types'))";
|
||||
}
|
||||
|
||||
if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
|
||||
return "(probability.exists() and (probability is decimal)) implies ((probability as decimal) <= 100)";
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package org.hl7.fhir.validation.instance.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
|
@ -11,6 +13,8 @@ import org.hl7.fhir.utilities.validation.ValidationMessage;
|
|||
|
||||
public class ValidationContext {
|
||||
|
||||
public static final String INTERNAL_REFERENCES_NAME = "internal.references";
|
||||
|
||||
private Object appContext;
|
||||
|
||||
// the version we are currently validating for right now
|
||||
|
@ -27,6 +31,7 @@ public class ValidationContext {
|
|||
|
||||
private boolean checkSpecials = true;
|
||||
private Map<String, List<ValidationMessage>> sliceRecords;
|
||||
private Set<String> internalRefs;
|
||||
|
||||
public ValidationContext(Object appContext) {
|
||||
this.appContext = appContext;
|
||||
|
@ -36,12 +41,22 @@ public class ValidationContext {
|
|||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = element;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
|
||||
// no groupingResource (Bundle or Parameters)
|
||||
dump("creating");
|
||||
}
|
||||
|
||||
private Set<String> setupInternalRefs(Element element) {
|
||||
Set<String> res = (Set<String>) element.getUserData(INTERNAL_REFERENCES_NAME);
|
||||
if (res == null) {
|
||||
res = new HashSet<String>();
|
||||
element.setUserData(INTERNAL_REFERENCES_NAME, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void check() {
|
||||
if (!rootResource.hasParentForValidator()) {
|
||||
throw new Error("No parent on root resource");
|
||||
|
@ -52,6 +67,7 @@ public class ValidationContext {
|
|||
this.appContext = appContext;
|
||||
this.resource = element;
|
||||
this.rootResource = root;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
// no groupingResource (Bundle or Parameters)
|
||||
dump("creating");
|
||||
|
@ -62,6 +78,7 @@ public class ValidationContext {
|
|||
this.resource = element;
|
||||
this.rootResource = root;
|
||||
this.groupingResource = groupingResource;
|
||||
this.internalRefs = setupInternalRefs(element);
|
||||
check();
|
||||
dump("creating");
|
||||
}
|
||||
|
@ -137,6 +154,7 @@ public class ValidationContext {
|
|||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(element);
|
||||
res.dump("forContained");
|
||||
return res;
|
||||
}
|
||||
|
@ -148,6 +166,7 @@ public class ValidationContext {
|
|||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(element);
|
||||
res.dump("forEntry");
|
||||
return res;
|
||||
}
|
||||
|
@ -159,6 +178,7 @@ public class ValidationContext {
|
|||
res.profile = profile;
|
||||
res.version = version;
|
||||
res.groupingResource = groupingResource;
|
||||
res.internalRefs = internalRefs;
|
||||
res.sliceRecords = sliceRecords != null ? sliceRecords : new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forProfile "+profile.getUrl());
|
||||
return res;
|
||||
|
@ -171,6 +191,7 @@ public class ValidationContext {
|
|||
res.profile = profile;
|
||||
res.groupingResource = groupingResource;
|
||||
res.checkSpecials = false;
|
||||
res.internalRefs = setupInternalRefs(resource);
|
||||
res.dump("forLocalReference "+profile.getUrl());
|
||||
res.version = version;
|
||||
return res;
|
||||
|
@ -191,6 +212,7 @@ public class ValidationContext {
|
|||
res.groupingResource = null;
|
||||
res.checkSpecials = false;
|
||||
res.version = version;
|
||||
res.internalRefs = setupInternalRefs(resource);
|
||||
res.dump("forRemoteReference "+profile.getUrl());
|
||||
return res;
|
||||
}
|
||||
|
@ -203,6 +225,7 @@ public class ValidationContext {
|
|||
res.profile = profile;
|
||||
res.checkSpecials = false;
|
||||
res.version = version;
|
||||
res.internalRefs = internalRefs;
|
||||
res.sliceRecords = new HashMap<String, List<ValidationMessage>>();
|
||||
res.dump("forSlicing");
|
||||
return res;
|
||||
|
@ -217,5 +240,9 @@ public class ValidationContext {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Set<String> getInternalRefs() {
|
||||
return internalRefs;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -6562,7 +6562,6 @@ v: {
|
|||
"error" : "The provided code 'http://loinc.org#5792-7' was not found in the value set 'http://hl7.org/fhir/ValueSet/birthDate'",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
|
@ -6586,3 +6585,68 @@ v: {
|
|||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "76534-7",
|
||||
"display" : "Systolic blood pressure by Noninvasive"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Systolic blood pressure by Noninvasive",
|
||||
"code" : "76534-7",
|
||||
"system" : "http://loinc.org",
|
||||
"version" : "2.74",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "76534-7"
|
||||
}, "url": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0", "version": "4.0.1", "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "Systolic blood pressure by Noninvasive",
|
||||
"code" : "76534-7",
|
||||
"severity" : "error",
|
||||
"error" : "The provided code 'http://loinc.org#76534-7' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r4"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "code-invalid",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-in-vs"
|
||||
}],
|
||||
"text" : "The provided code 'http://loinc.org#76534-7' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult--0|4.0.1'"
|
||||
},
|
||||
"location" : ["Coding.code"],
|
||||
"expression" : ["Coding.code"]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"system" : "http://hl7.org/fhir/smart-app-launch/CodeSystem/user-access-category",
|
||||
"code" : "laboratory",
|
||||
"display" : "Laboratory"
|
||||
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"code" : "laboratory",
|
||||
"severity" : "error",
|
||||
"error" : "A definition for CodeSystem 'http://hl7.org/fhir/smart-app-launch/CodeSystem/user-access-category' could not be found, so the code cannot be validated",
|
||||
"class" : "CODESYSTEM_UNSUPPORTED",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "http://hl7.org/fhir/smart-app-launch/CodeSystem/user-access-category",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
"extension" : [{
|
||||
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
|
||||
"valueUrl" : "http://tx-dev.fhir.org/r4"
|
||||
}],
|
||||
"severity" : "error",
|
||||
"code" : "not-found",
|
||||
"details" : {
|
||||
"coding" : [{
|
||||
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code" : "not-found"
|
||||
}],
|
||||
"text" : "A definition for CodeSystem 'http://hl7.org/fhir/smart-app-launch/CodeSystem/user-access-category' could not be found, so the code cannot be validated"
|
||||
},
|
||||
"location" : ["Coding.system"],
|
||||
"expression" : ["Coding.system"]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
|
@ -8,6 +8,7 @@
|
|||
"server" : "https://tx.ontoserver.csiro.au/fhir",
|
||||
"filename" : "vs-2a3253d9-d12e-45f3-877e-0dc2654b3904.json"
|
||||
},
|
||||
"http://hl7.org/fhir/smart-app-launch/ValueSet/user-access-category" : null,
|
||||
"http://fhir.ch/ig/ch-ig/ValueSet/OrganizationType" : null,
|
||||
"http://loinc.org/vs/LL4048-6" : null,
|
||||
"https://fhir.kbv.de/ValueSet/KBV_VS_SFHIR_ICD_SEITENLOKALISATION" : null
|
||||
|
|
|
@ -214,7 +214,6 @@ v: {
|
|||
"code" : "text/plain",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
@ -239,7 +238,6 @@ v: {
|
|||
"error" : "The provided code 'http://snomed.info/sct#271649006 ('Systolic blood pressure')' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-vitalsignresult|5.0.0'",
|
||||
"class" : "UNKNOWN",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
|
@ -277,7 +275,6 @@ v: {
|
|||
"code" : "json",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
@ -299,7 +296,6 @@ v: {
|
|||
"code" : "001",
|
||||
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
|
@ -335,7 +331,6 @@ v: {
|
|||
"code" : "nl-NL",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
@ -356,7 +351,6 @@ v: {
|
|||
"code" : "en-AU",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
@ -377,7 +371,6 @@ v: {
|
|||
"code" : "en",
|
||||
"system" : "urn:ietf:bcp:47",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
@ -400,7 +393,6 @@ v: {
|
|||
"code" : "001",
|
||||
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome",
|
||||
"issue" : [{
|
||||
|
@ -422,3 +414,24 @@ v: {
|
|||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
{"code" : {
|
||||
"code" : "image/gif"
|
||||
}, "url": "http://hl7.org/fhir/ValueSet/mimetypes", "version": "5.0.0", "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"true", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||
"resourceType" : "Parameters",
|
||||
"parameter" : [{
|
||||
"name" : "profile-url",
|
||||
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||
}]
|
||||
}}####
|
||||
v: {
|
||||
"display" : "image/gif",
|
||||
"code" : "image/gif",
|
||||
"system" : "urn:ietf:bcp:13",
|
||||
"server" : "http://tx-dev.fhir.org/r4",
|
||||
"unknown-systems" : "",
|
||||
"issues" : {
|
||||
"resourceType" : "OperationOutcome"
|
||||
}
|
||||
|
||||
}
|
||||
-------------------------------------------------------------------------------------
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -20,7 +20,7 @@
|
|||
<properties>
|
||||
<guava_version>32.0.1-jre</guava_version>
|
||||
<hapi_fhir_version>6.4.1</hapi_fhir_version>
|
||||
<validator_test_case_version>1.4.28</validator_test_case_version>
|
||||
<validator_test_case_version>1.4.29-SNAPSHOT</validator_test_case_version>
|
||||
<jackson_version>2.16.0</jackson_version>
|
||||
<junit_jupiter_version>5.9.2</junit_jupiter_version>
|
||||
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>
|
||||
|
|
Loading…
Reference in New Issue