Merge pull request #1442 from hapifhir/2023-09-gg-misc-fixes
2023 09 gg misc fixes
This commit is contained in:
commit
c47095254f
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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+"}";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
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.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>
|
||||
|
|
Loading…
Reference in New Issue