Merge pull request #1442 from hapifhir/2023-09-gg-misc-fixes

2023 09 gg misc fixes
This commit is contained in:
Grahame Grieve 2023-09-21 15:28:25 +10:00 committed by GitHub
commit c47095254f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 278 additions and 105 deletions

View File

@ -1,7 +1,23 @@
## Validator Changes
* no changes
* Significant Performance improvements parsing JSON resources
* Refactor Type handling for faster performance
* Validate the stated publisher/WG/contacts for HL7 published resources
* Better error message when diff contains bad paths
* pass dependent resources to server and make sure cache-id is filled out properly in all contexts
* Fix error in FML parser parsing parameters
* Fix issue with dom-6 and contained elements (Internal ChildMap synchro issues)
* Better handling of errors from tx.fhir.org
* Fix bug checking for implicit value sets
* Fix bug checking of mixing snomed display types
* Reduce size of validatable concept map to 500 - for now + better handling of errors on server batches
* Improve UCUM validation BP rule
## Other code changes
* no changes
* Fix up handling of includes in liquid templates
* Fix up rendering of profile names for abstract profile instantiations
* Improved rendering of codes in include when rendering valuesets
* Start generating .index.db as well as .index.json in packages for faster package reading
* Fix problem caching look up of implied value sets
* Add okio dependency for running vsac

View File

@ -18,6 +18,7 @@ import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.utils.ResourceUtilities;
import org.hl7.fhir.r4.utils.client.EFhirClientException;
import org.hl7.fhir.r4.utils.client.ResourceFormat;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.settings.FhirSettings;
import okhttp3.Authenticator;
@ -247,7 +248,7 @@ public class FhirRequestBuilder {
public Bundle executeAsBatch() throws IOException {
formatHeaders(httpRequest, resourceFormat, null);
Response response = getHttpClient().newCall(httpRequest.build()).execute();
return unmarshalFeed(response, resourceFormat);
return unmarshalFeed(response, resourceFormat);
}
/**
@ -307,6 +308,12 @@ public class FhirRequestBuilder {
}
}
}
if (!response.isSuccessful() && feed == null && error == null) {
String text = TextFile.bytesToString(body);
throw new EFhirClientException("Error from "+source+": " + text);
}
} catch (EFhirClientException e) {
throw e;
} catch (IOException ioe) {
throw new EFhirClientException("Error reading Http Response from "+source+":"+ioe.getMessage(), ioe);
} catch (Exception e) {

View File

@ -1013,17 +1013,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (batch.getEntry().size() > 0) {
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
if (tcc.getClient() == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
if (txLog != null) {
txLog.clearLastId();
}
Bundle resp = tcc.getClient().validateBatch(batch);
if (resp == null) {
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
}
Bundle resp = processBatch(batch, systems);
for (int i = 0; i < batch.getEntry().size(); i++) {
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
BundleEntryComponent r = resp.getEntry().get(i);
@ -1039,6 +1029,21 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
}
private Bundle processBatch(Bundle batch, Set<String> systems) {
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
if (tcc.getClient() == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
if (txLog != null) {
txLog.clearLastId();
}
Bundle resp = tcc.getClient().validateBatch(batch);
if (resp == null) {
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
}
return resp;
}
@Override
public void validateCodeBatchByRef(ValidationOptions options, List<? extends CodingValidationRequest> codes, String vsUrl) {
@ -1113,17 +1118,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (batch.getEntry().size() > 0) {
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
if (tcc.getClient() == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
if (txLog != null) {
txLog.clearLastId();
}
Bundle resp = tcc.getClient().validateBatch(batch);
if (resp == null) {
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
}
Bundle resp = processBatch(batch, systems);
for (int i = 0; i < batch.getEntry().size(); i++) {
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
BundleEntryComponent r = resp.getEntry().get(i);

View File

@ -267,7 +267,9 @@ public class TerminologyCache {
if (vs != null && vs.hasUrl() && vs.hasVersion()) {
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"url\": \""+Utilities.escapeJson(vs.getUrl())
+"\", \"version\": \""+Utilities.escapeJson(vs.getVersion())+"\""+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}\r\n";
+"\", \"version\": \""+Utilities.escapeJson(vs.getVersion())+"\""+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}\r\n";
} else if (options.getVsAsUrl()) {
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+extracted(json, vs)+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}";
} else {
ValueSet vsc = getVSEssense(vs);
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : extracted(json, vsc))+(options == null ? "" : ", "+options.toJson())+", \"profile\": "+expJS+"}";

View File

@ -54,6 +54,8 @@ import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
import org.hl7.fhir.utilities.NamedItemList;
import org.hl7.fhir.utilities.NamedItemList.NamedItem;
import org.hl7.fhir.utilities.SourceLocation;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -69,7 +71,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
* @author Grahame Grieve
*
*/
public class Element extends Base {
public class Element extends Base implements NamedItem {
private static final HashSet<String> extensionList = new HashSet<>(Arrays.asList("extension", "modifierExtension"));
public enum SpecialElement {
@ -109,7 +111,7 @@ public class Element extends Base {
private String type;
private String value;
private int index = -1;
private List<Element> children;
private NamedItemList<Element> children;
private Property property;
private Property elementProperty; // this is used when special is set to true - it tracks the underlying element property which is used in a few places
private int line;
@ -123,7 +125,6 @@ public class Element extends Base {
private List<ValidationMessage> messages;
private boolean prohibited;
private boolean required;
private Map<String, List<Element>> childMap;
private int descendentCount;
private int instanceId;
private boolean isNull;
@ -149,7 +150,7 @@ public class Element extends Base {
this.name = name;
this.property = property;
if (property.isResource()) {
children = new ArrayList<>();
children = new NamedItemList<>();
}
}
@ -190,9 +191,9 @@ public class Element extends Base {
return !(children == null || children.isEmpty());
}
public List<Element> getChildren() {
public NamedItemList<Element> getChildren() {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
return children;
}
@ -233,21 +234,7 @@ public class Element extends Base {
}
public List<Element> getChildrenByName(String name) {
List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
res.addAll(l);
}
} else {
if (hasChildren()) {
for (Element child : children)
if (name.equals(child.getName()))
res.add(child);
}
}
return res;
return children.getByName(name);
}
public void numberChildren() {
@ -308,7 +295,7 @@ public class Element extends Base {
public void setChildValue(String name, String value) {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
for (Element child : children) {
if (name.equals(child.getName())) {
if (!child.isPrimitive())
@ -316,7 +303,7 @@ public class Element extends Base {
child.setValue(value);
}
}
childMap = null;
try {
setProperty(name.hashCode(), name, new StringType(value));
} catch (FHIRException e) {
@ -327,8 +314,7 @@ public class Element extends Base {
public List<Element> getChildren(String name) {
List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
res.addAll(l);
}
@ -367,8 +353,7 @@ public class Element extends Base {
List<Base> result = new ArrayList<Base>();
if (children != null) {
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
result.addAll(l);
}
@ -389,27 +374,6 @@ public class Element extends Base {
return result.toArray(new Base[result.size()]);
}
private void populateChildMap() {
if (childMap == null) {
childMap = new HashMap<>();
for (Element child : children) {
String n;
if (child.getProperty().getName().endsWith("[x]")) {
n = child.getProperty().getName();
n = n.substring(0, n.length()-3);
} else {
n = child.getName();
}
List<Element> l = childMap.get(n);
if (l == null) {
l = new ArrayList<Element>();
childMap.put(n,l);
}
l.add(child);
}
}
}
@Override
protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) {
if (children != null) {
@ -446,9 +410,8 @@ public class Element extends Base {
throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type");
}
childMap = null;
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
Element childForValue = null;
// look through existing children
@ -505,7 +468,7 @@ public class Element extends Base {
}
if (ve.children != null) {
if (childForValue.children == null)
childForValue.children = new ArrayList<Element>();
childForValue.children = new NamedItemList<Element>();
else
childForValue.children.clear();
childForValue.children.addAll(ve.children);
@ -533,7 +496,7 @@ public class Element extends Base {
public Element makeElement(String name) throws FHIRException {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
// look through existing children
for (Element child : children) {
@ -572,7 +535,7 @@ public class Element extends Base {
public Element forceElement(String name) throws FHIRException {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
// look through existing children
for (Element child : children) {
@ -669,7 +632,6 @@ public class Element extends Base {
for (Element e : children) {
e.clearDecorations();
}
childMap = null;
}
public void markValidation(StructureDefinition profile, ElementDefinition definition) {
@ -694,9 +656,8 @@ public class Element extends Base {
if (children == null)
return null;
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l == null) {
List<Element> l = children.getByName(name);
if (l == null || l.size() == 0) {
// try the other way (in case of complicated naming rules)
} else if (l.size() > 1) {
throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
@ -724,8 +685,7 @@ public class Element extends Base {
public void getNamedChildren(String name, List<Element> list) {
if (children != null)
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
list.addAll(l);
}
@ -918,8 +878,7 @@ public class Element extends Base {
remove.add(child);
}
children.removeAll(remove);
Collections.sort(children, new ElementSortComparator(this, this.property));
childMap = null;
children.sort(new ElementSortComparator(this, this.property));
}
}
@ -1125,7 +1084,6 @@ public class Element extends Base {
public void clear() {
comments = null;
children.clear();
childMap = null;
property = null;
elementProperty = null;
xhtml = null;
@ -1157,7 +1115,6 @@ public class Element extends Base {
public void removeChild(String name) {
children.removeIf(n -> name.equals(n.getName()));
childMap = null;
}
public boolean isProhibited() {
@ -1331,7 +1288,7 @@ public class Element extends Base {
public Element addElement(String name) {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
for (Property p : property.getChildProperties(this.name, type)) {
if (p.getName().equals(name)) {
@ -1367,13 +1324,13 @@ public class Element extends Base {
}
dest.value = value;
if (children != null) {
dest.children = new ArrayList<>();
dest.children = new NamedItemList<>();
for (Element child : children) {
dest.children.add((Element) child.copy());
}
} else {
dest.children = null;
}
}
dest.line = line;
dest.col = col;
dest.xhtml = xhtml;
@ -1383,7 +1340,6 @@ public class Element extends Base {
dest.messages = null;
dest.prohibited = prohibited;
dest.required = required;
dest.childMap = null;
dest.descendentCount = descendentCount;
dest.instanceId = instanceId;
dest.isNull = isNull;
@ -1499,4 +1455,15 @@ public class Element extends Base {
ext.addElement("url").setValue("value");
ext.addElement("valueString").setValue(translation);
}
@Override
public String getListName() {
if (getProperty().getName().endsWith("[x]")) {
String n = getProperty().getName();
return n.substring(0, n.length()-3);
} else {
return getName();
}
}
}

View File

@ -0,0 +1,140 @@
package org.hl7.fhir.utilities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class NamedItemList<T extends org.hl7.fhir.utilities.NamedItemList.NamedItem> implements Collection<T> {
public interface NamedItem {
public String getListName();
}
private static final int SIZE_CUTOFF_MAP = 10;
private List<T> list = new ArrayList<>();
private Map<String, List<T>> map = null;
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(T e) {
map = null;
return list.add(e);
}
public void add(int index, T e) {
list.add(index, e);
map = null;
}
@Override
public boolean remove(Object o) {
map = null;
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
map = null;
return list.addAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
map = null;
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
map = null;
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
map = null;
}
public List<T> getByName(String name) {
List<T> res = new ArrayList<>();
if (size() > SIZE_CUTOFF_MAP) {
if (map == null) {
buildMap();
}
List<T> l = map.get(name);
if (l != null) {
res.addAll(l);
}
} else {
for (T child : list) {
if (name.equals(child.getListName())) {
res.add(child);
}
}
}
return res;
}
public T get(int c) {
return list.get(c);
}
private void buildMap() {
map = new HashMap<>();
for (T child : list) {
String n = child.getListName();
List<T> l = map.get(n);
if (l == null) {
l = new ArrayList<>();
map.put(n,l);
}
l.add(child);
}
}
public void sort(Comparator<? super T> sorter) {
Collections.sort(list, sorter);
}
}

View File

@ -997,6 +997,9 @@ public class I18nConstants {
public static final String VALIDATION_HL7_PUBLISHER_MISMATCH = "VALIDATION_HL7_PUBLISHER_MISMATCH";
public static final String VALIDATION_HL7_WG_URL = "VALIDATION_HL7_WG_URL";
public static final String VALIDATION_HL7_PUBLISHER_MISSING = "VALIDATION_HL7_PUBLISHER_MISSING";
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS = "TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS";
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NO_UNIT = "TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NO_UNIT";
public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NOT_IN_UNIT = "TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NOT_IN_UNIT";
}

View File

@ -688,7 +688,6 @@ FHIRPATH_CONTINUOUS_ONLY= Error evaluating FHIRPath expression: The function {0}
FHIRPATH_FOCUS_one =
FHIRPATH_FOCUS_other = Error evaluating FHIRPath expression: focus for {0} can only have one value, but has {0} values
REFERENCE_REF_SUSPICIOUS = The syntax of the reference ''{0}'' looks incorrect, and it should be checked
TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading. Best Practice is not to use annotations in the UCUM code, and rather to make sure that Quantity.unit is correctly human readable
XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Invalid element name inside in a paragraph in the XHTML (''{0}'')
UNSUPPORTED_IDENTIFIER_PATTERN_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported property {3} on type {2} for pattern for discriminator ({0}) for slice {1}
UNSUPPORTED_IDENTIFIER_PATTERN_NO_PROPERTY_NOT_SUPPORTED_FOR_DISCRIMINATOR_FOR_SLICE = Unsupported: no properties with values found on type {2} for pattern for discriminator ({0}) for slice {1}
@ -949,8 +948,8 @@ SD_OBGLIGATION_INHERITS_PROFILE_TARGET_NOT_FOUND = The profile ''{0}'' could not
SD_OBGLIGATION_INHERITS_PROFILE_NOT_RIGHT_TYPE = The profile ''{0}'' is not marked as an obligation profile
SD_OBGLIGATION_INHERITS_PROFILE_NOT_RIGHT_BASE = The profile ''{0}'' has a different base ''{1}'' from that expected ''{2}''
RND_CS_CONTENT_COMPLETE = This <param name="cased"/> code system <param name="cs"/> defines the following code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_EXAMPLE = This <param name="cased"/> code system <param name="cs"/> provides some example code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_FRAGMENT = This <param name="cased"/> code system <param name="cs"/> provides a fragment that includes following code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_EXAMPLE = This <param name="cased"/> code system <param name="cs"/> provides some code<if test="code-count != 1">s</if><param name="h"/> <b>that are example only</b>:
RND_CS_CONTENT_FRAGMENT = This <param name="cased"/> code system <param name="cs"/> provides <b>a fragment<b> that includes following code<if test="code-count != 1">s</if><param name="h"/>:
RND_CS_CONTENT_NOTPRESENT = This <param name="cased"/> code system <param name="cs"/> defines codes<param name="h"/>, but no codes are represented here
RND_CS_CONTENT_SUPPLEMENT = This code system <param name="cs"/> defines {0} on the following code<if test="code-count != 1">s</if>:
QUESTIONNAIRE_Q_UNKNOWN_DERIVATION = The questionnaire ''{0}'' referred to in the derivation could not be found
@ -984,7 +983,7 @@ TERMINOLOGY_TX_WARNING = {1}
SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended (examples for FSN: {0} and examples for no FSN: {1})
CS_SCT_IPS_NOT_IPS = The Snomed CT code {0} ({1}) is not a member of the IPS free set
UNICODE_XML_BAD_CHARS_one = This content includes the character {1} (hex value). This character is illegal in the XML version of FHIR, and there is generally no valid use for such characters
UNICODE_XML_BAD_CHARS_other = This content includes the characters {1} (hex values). These characters are illegal in the XML version of FHIR, and there is generally no valid use for such characters
@ -1055,3 +1054,6 @@ VALIDATION_HL7_WG_UNKNOWN = The nominated WG ''{0}'' is unknown
VALIDATION_HL7_PUBLISHER_MISMATCH = The nominated WG ''{0}'' means that the publisher should be ''{1}'' but ''{2}'' was found
VALIDATION_HL7_WG_URL = The nominated WG ''{0}'' means that the contact url should be ''{1}'' but it was not found
VALIDATION_HL7_PUBLISHER_MISSING = When HL7 is publishing a resource, the publisher must be provided, and for WG ''{0}'' it should be ''{1}''
TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NO_UNIT = UCUM Codes that contain human readable annotations like {0} can be misleading (e.g. they are ignored when comparing units). Best Practice is not to depend on annotations in the UCUM code, so this usage should be checked, and the Quantity.unit SHOULD contain the annotation
TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NOT_IN_UNIT = UCUM Codes that contain human readable annotations like {0} can be misleading (e.g. they are ignored when comparing units). Best Practice is not to depend on annotations in the UCUM code, so this usage should be checked, and the Quantity.unit ''{1}'' SHOULD contain the annotation (it does not)
TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS = UCUM Codes that contain human readable annotations like {0} can be misleading (e.g. they are ignored when comparing units). Best Practice is not to depend on annotations in the UCUM code, so this usage should be checked

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.validation.codesystem;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.context.IWorkerContext;
@ -13,7 +14,9 @@ import org.hl7.fhir.validation.instance.utils.NodeStack;
public class SnomedCTChecker extends CodeSystemChecker {
private boolean noTag = false;
private List<String> noTags = new ArrayList<>();
private boolean hasTag = false;
private List<String> tags = new ArrayList<>();
public SnomedCTChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List<ValidationMessage> errors) {
super(context, xverManager, debug, errors);
@ -22,16 +25,24 @@ public class SnomedCTChecker extends CodeSystemChecker {
public void checkConcept(String code, String display) {
super.checkConcept(code, display);
if (!Utilities.noString(display)) {
boolean tagged = display.endsWith(")") && display.indexOf("(") > display.length() - 20;
int s = display.lastIndexOf("(");
int e = display.lastIndexOf(")");
boolean tagged = e == display.length() - 1 && s > -1 && s > display.length() - 20;
if (tagged) {
hasTag = true;
if (tags.size() < 5) {
tags.add(display);
}
} else {
noTag = true;
if (noTags.size() < 5) {
noTags.add(display);
}
}
}
}
public void finish(Element inc, NodeStack stack) {
super.finish(inc, stack);
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED);
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED, tags.toString(), noTags.toString());
}
}

View File

@ -3363,7 +3363,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
int b = code.indexOf("{");
int e = code.indexOf("}");
if (b >= 0 && e > 0 && b < e) {
ok = bpCheck(errors, IssueType.BUSINESSRULE, element.line(), element.col(), path, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS, code.substring(b, e+1)) && ok;
String annotation = code.substring(b, e+1);
String annotationValue = code.substring(b+1, e);
if (unit == null) {
ok = bpCheck(errors, IssueType.BUSINESSRULE, element.line(), element.col(), path, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NO_UNIT, annotation) && ok;
} else if (!unit.toLowerCase().contains(annotationValue.toLowerCase())) {
ok = bpCheck(errors, IssueType.BUSINESSRULE, element.line(), element.col(), path, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS_NOT_IN_UNIT, annotation, unit) && ok;
} else {
ok = bpCheck(errors, IssueType.BUSINESSRULE, element.line(), element.col(), path, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_UCUM_ANNOTATIONS, annotation, unit) && ok;
}
}
}

View File

@ -31,7 +31,7 @@ import org.hl7.fhir.validation.instance.utils.NodeStack;
public class ConceptMapValidator extends BaseValidator {
private static final int TOO_MANY_CODES_TO_VALIDATE = 1000;
private static final int TOO_MANY_CODES_TO_VALIDATE = 500;
public static class PropertyDefinition {
private String type;

View File

@ -35,6 +35,7 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
@ -815,7 +816,7 @@ public class StructureDefinitionValidator extends BaseValidator {
private boolean serverSupportsValueSet(String ref) {
ValidationResult vr = context.validateCode(new ValidationOptions().withCheckValueSetOnly().withVsAsUrl().withNoClient(), new Coding("http://loinc.org", "5792-7", null), new ValueSet().setUrl(ref));
return vr.getErrorClass() == null;
return vr.getErrorClass() == null || vr.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN;
}
private boolean validateElementType(List<ValidationMessage> errors, Element type, NodeStack stack, StructureDefinition sd, String path, boolean logical) {

View File

@ -4077,7 +4077,6 @@ v: {
"code" : "34133-9",
"system" : "http://loinc.org",
"version" : "2.74",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
@ -4100,7 +4099,29 @@ v: {
"code" : "18842-5",
"system" : "http://loinc.org",
"version" : "2.74",
"unknown-systems" : "",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "5792-7"
}, "valueSet" :{
"resourceType" : "ValueSet",
"url" : "http://hl7.org/fhir/ValueSet/birthDate"
}, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"severity" : "error",
"error" : "Error from http://tx-dev.fhir.org/r4: Unable to resolve value set \"http://hl7.org/fhir/ValueSet/birthDate\"",
"class" : "SERVER_ERROR",
"issues" : {
"resourceType" : "OperationOutcome"
}

View File

@ -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.5-SNAPSHOT</validator_test_case_version>
<validator_test_case_version>1.4.5</validator_test_case_version>
<jackson_version>2.15.2</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>