Merge pull request #1271 from hapifhir/gg-202304-more-snapshot
Gg 202304 more snapshot
This commit is contained in:
commit
8f3367772c
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
* Snapshot Generation Changes:
|
* Snapshot Generation Changes:
|
||||||
** Check for slicenames without any slicing
|
** Check for slicenames without any slicing
|
||||||
|
** Check that slice names are unique
|
||||||
** Check for additional slicing rules in a set of slices
|
** Check for additional slicing rules in a set of slices
|
||||||
** Check that the minimum cardinality of a set of slices is correct
|
** Check that the minimum cardinality of a set of slices is correct
|
||||||
* Clean up duplicate errors when dates/dateTimes/instants have invalid formats
|
* Clean up duplicate errors when dates/dateTimes/instants have invalid formats
|
||||||
* Handle unknown code systems consistently when validating coded elements
|
* Handle unknown code systems consistently when validating coded elements
|
||||||
|
* Handle sub-slicing case where slice matches both the slice definition and the sub-slice definition
|
||||||
|
|
||||||
## Other code changes
|
## Other code changes
|
||||||
|
|
||||||
* Add support for R4B to loader
|
* Add support for R4B to loader
|
||||||
|
* Change type if cache-id parameter
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -174,17 +174,13 @@ public class ProfilePathProcessor {
|
||||||
// in the simple case, source is not sliced.
|
// in the simple case, source is not sliced.
|
||||||
if (!currentBase.hasSlicing() || currentBasePath.equals(getSlicing().getPath()))
|
if (!currentBase.hasSlicing() || currentBasePath.equals(getSlicing().getPath()))
|
||||||
{
|
{
|
||||||
ElementDefinition currentRes = processSimplePath(currentBase, currentBasePath, diffMatches, typeList,
|
ElementDefinition currentRes = processSimplePath(currentBase, currentBasePath, diffMatches, typeList, cursors);
|
||||||
cursors
|
|
||||||
);
|
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
res = currentRes;
|
res = currentRes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
processPathWithSlicedBase(currentBase, currentBasePath, diffMatches, typeList,
|
processPathWithSlicedBase(currentBase, currentBasePath, diffMatches, typeList, cursors);
|
||||||
cursors
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
public class ElementDefinitionCounter {
|
public class ElementDefinitionCounter {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
ElementDefinition focus;
|
ElementDefinition focus;
|
||||||
|
Set<String> names = new HashSet<>();
|
||||||
|
|
||||||
public ElementDefinitionCounter(ElementDefinition ed) {
|
public ElementDefinitionCounter(ElementDefinition ed) {
|
||||||
focus = ed;
|
focus = ed;
|
||||||
|
@ -142,15 +143,16 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
|
|
||||||
public int update() {
|
public int update() {
|
||||||
if (count > focus.getMin()) {
|
if (count > focus.getMin()) {
|
||||||
int was = focus.getMin();
|
return count;
|
||||||
focus.setMin(count);
|
|
||||||
return was;
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void count(ElementDefinition ed) {
|
public boolean count(ElementDefinition ed, String name) {
|
||||||
count = count + ed.getMin();
|
count = count + ed.getMin();
|
||||||
|
boolean ok = !names.contains(name);
|
||||||
|
names.add(name);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ElementDefinition getFocus() {
|
public ElementDefinition getFocus() {
|
||||||
|
@ -725,13 +727,13 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
if (tn.contains("/")) {
|
if (tn.contains("/")) {
|
||||||
tn = tn.substring(tn.lastIndexOf("/")+1);
|
tn = tn.substring(tn.lastIndexOf("/")+1);
|
||||||
}
|
}
|
||||||
System.out.println("Check slicing for "+derived.getVersionedUrl());
|
// System.out.println("Check slicing for "+derived.getVersionedUrl());
|
||||||
Map<String, ElementDefinitionCounter> slices = new HashMap<>();
|
Map<String, ElementDefinitionCounter> slices = new HashMap<>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
|
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
|
||||||
if (ed.hasSlicing()) {
|
if (ed.hasSlicing()) {
|
||||||
slices.put(ed.getPath(), new ElementDefinitionCounter(ed));
|
slices.put(ed.getPath(), new ElementDefinitionCounter(ed));
|
||||||
System.out.println("Entering slicing for "+ed.getPath()+" ["+i+"]");
|
// System.out.println("Entering slicing for "+ed.getPath()+" ["+i+"]");
|
||||||
} else {
|
} else {
|
||||||
Set<String> toRemove = new HashSet<>();
|
Set<String> toRemove = new HashSet<>();
|
||||||
for (String s : slices.keySet()) {
|
for (String s : slices.keySet()) {
|
||||||
|
@ -740,13 +742,14 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (String s : toRemove) {
|
for (String s : toRemove) {
|
||||||
int was = slices.get(s).update();
|
int count = slices.get(s).update();
|
||||||
if (was > -1) {
|
if (count > -1) {
|
||||||
String msg = "The slice definition for "+slices.get(s).getFocus().getId()+" had a minimum of "+was+" but the slices added up to a minimum of "+slices.get(s).getFocus().getMin()+" so the value has been adjusted in the snapshot";
|
String msg = "The slice definition for "+slices.get(s).getFocus().getId()+" has a minimum of "+slices.get(s).getFocus().getMin()+" but the slices add up to a minimum of "+count;
|
||||||
System.out.println(msg);
|
//+" so the value has been adjusted in the snapshot"; we don't adjust it because of downstream effects. But if it's for publication, they better get it right.
|
||||||
|
// System.out.println(msg);
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION));
|
||||||
}
|
}
|
||||||
System.out.println("Exiting slicing for "+s+" at "+ed.getPath()+" ["+i+"]");
|
// System.out.println("Exiting slicing for "+s+" at "+ed.getPath()+" ["+i+"]");
|
||||||
slices.remove(s);
|
slices.remove(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -758,7 +761,10 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
|
||||||
}
|
}
|
||||||
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
|
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
|
||||||
slices.get(ed.getPath()).count(ed);
|
if (!slices.get(ed.getPath()).count(ed, ed.getSliceName())) {
|
||||||
|
String msg = "Duplicate slice name "+ed.getSliceName()+" on "+ed.getId()+" (["+i+"])";
|
||||||
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -2043,10 +2049,15 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
}
|
}
|
||||||
// Before applying changes, apply them to what's in the profile
|
// Before applying changes, apply them to what's in the profile
|
||||||
StructureDefinition profile = null;
|
StructureDefinition profile = null;
|
||||||
if (base.hasSliceName())
|
if (base.hasSliceName()) {
|
||||||
profile = base.getType().size() == 1 && base.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, base.getTypeFirstRep().getProfile().get(0).getValue(), srcSD) : null;
|
profile = base.getType().size() == 1 && base.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, base.getTypeFirstRep().getProfile().get(0).getValue(), srcSD) : null;
|
||||||
if (profile==null)
|
}
|
||||||
|
if (profile==null) {
|
||||||
profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue(), derivedSrc) : null;
|
profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue(), derivedSrc) : null;
|
||||||
|
if (profile != null && !"Extension".equals(profile.getType())) {
|
||||||
|
profile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
ElementDefinition e = profile.getSnapshot().getElement().get(0);
|
ElementDefinition e = profile.getSnapshot().getElement().get(0);
|
||||||
String webroot = profile.getUserString("webroot");
|
String webroot = profile.getUserString("webroot");
|
||||||
|
@ -2157,7 +2168,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
if (derived.hasMinElement()) {
|
if (derived.hasMinElement()) {
|
||||||
if (!Base.compareDeep(derived.getMinElement(), base.getMinElement(), false)) {
|
if (!Base.compareDeep(derived.getMinElement(), base.getMinElement(), false)) {
|
||||||
if (derived.getMin() < base.getMin() && !derived.hasSliceName()) // in a slice, minimum cardinality rules do not apply
|
if (derived.getMin() < base.getMin() && !derived.hasSliceName()) // in a slice, minimum cardinality rules do not apply
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than base min ("+Integer.toString(base.getMin())+")", ValidationMessage.IssueSeverity.ERROR));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived min ("+Integer.toString(derived.getMin())+") cannot be less than the base min ("+Integer.toString(base.getMin())+") in "+srcSD.getVersionedUrl(), ValidationMessage.IssueSeverity.ERROR));
|
||||||
base.setMinElement(derived.getMinElement().copy());
|
base.setMinElement(derived.getMinElement().copy());
|
||||||
} else if (trimDifferential)
|
} else if (trimDifferential)
|
||||||
derived.setMinElement(null);
|
derived.setMinElement(null);
|
||||||
|
@ -2168,7 +2179,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
if (derived.hasMaxElement()) {
|
if (derived.hasMaxElement()) {
|
||||||
if (!Base.compareDeep(derived.getMaxElement(), base.getMaxElement(), false)) {
|
if (!Base.compareDeep(derived.getMaxElement(), base.getMaxElement(), false)) {
|
||||||
if (isLargerMax(derived.getMax(), base.getMax()))
|
if (isLargerMax(derived.getMax(), base.getMax()))
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+source.getPath(), "Element "+base.getPath()+": derived max ("+derived.getMax()+") cannot be greater than the base max ("+base.getMax()+")", ValidationMessage.IssueSeverity.ERROR));
|
||||||
base.setMaxElement(derived.getMaxElement().copy());
|
base.setMaxElement(derived.getMaxElement().copy());
|
||||||
} else if (trimDifferential)
|
} else if (trimDifferential)
|
||||||
derived.setMaxElement(null);
|
derived.setMaxElement(null);
|
||||||
|
|
|
@ -73,6 +73,7 @@ import org.hl7.fhir.r5.model.DomainResource;
|
||||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||||
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
|
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
|
||||||
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
|
||||||
|
import org.hl7.fhir.r5.model.IdType;
|
||||||
import org.hl7.fhir.r5.model.ImplementationGuide;
|
import org.hl7.fhir.r5.model.ImplementationGuide;
|
||||||
import org.hl7.fhir.r5.model.Library;
|
import org.hl7.fhir.r5.model.Library;
|
||||||
import org.hl7.fhir.r5.model.Measure;
|
import org.hl7.fhir.r5.model.Measure;
|
||||||
|
@ -868,7 +869,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
|
|
||||||
// if that failed, we try to expand on the server
|
// if that failed, we try to expand on the server
|
||||||
if (addDependentResources(p, vs)) {
|
if (addDependentResources(p, vs)) {
|
||||||
p.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
|
p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noTerminologyServer) {
|
if (noTerminologyServer) {
|
||||||
|
@ -1147,7 +1148,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
|
|
||||||
boolean cached = addDependentResources(p, vs);
|
boolean cached = addDependentResources(p, vs);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
p.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
|
p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -1276,7 +1277,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
addDependentResources(pin, vs);
|
addDependentResources(pin, vs);
|
||||||
}
|
}
|
||||||
if (cache) {
|
if (cache) {
|
||||||
pin.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
|
pin.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
|
||||||
}
|
}
|
||||||
for (ParametersParameterComponent pp : pin.getParameter()) {
|
for (ParametersParameterComponent pp : pin.getParameter()) {
|
||||||
if (pp.getName().equals("profile")) {
|
if (pp.getName().equals("profile")) {
|
||||||
|
@ -1917,6 +1918,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
private Set<String> notCanonical = new HashSet<String>();
|
private Set<String> notCanonical = new HashSet<String>();
|
||||||
|
|
||||||
protected IWorkerContextManager.IPackageLoadingTracker packageTracker;
|
protected IWorkerContextManager.IPackageLoadingTracker packageTracker;
|
||||||
|
private boolean forPublication;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource fetchResourceById(String type, String uri) {
|
public Resource fetchResourceById(String type, String uri) {
|
||||||
|
@ -2467,4 +2469,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return new PEBuilder(this, elementProps, fixedProps);
|
return new PEBuilder(this, elementProps, fixedProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForPublication() {
|
||||||
|
return forPublication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForPublication(boolean value) {
|
||||||
|
forPublication = value;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,7 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||||
ProfileUtilities pu = new ProfileUtilities(context, msgs, this);
|
ProfileUtilities pu = new ProfileUtilities(context, msgs, this);
|
||||||
pu.setAutoFixSliceNames(true);
|
pu.setAutoFixSliceNames(true);
|
||||||
pu.setThrowException(false);
|
pu.setThrowException(false);
|
||||||
|
pu.setForPublication(context.isForPublication());
|
||||||
if (xverManager == null) {
|
if (xverManager == null) {
|
||||||
xverManager = new XVerExtensionManager(context);
|
xverManager = new XVerExtensionManager(context);
|
||||||
}
|
}
|
||||||
|
@ -282,8 +283,9 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
|
||||||
pu.sortDifferential(sd, p, p.getUrl(), errors, true);
|
pu.sortDifferential(sd, p, p.getUrl(), errors, true);
|
||||||
}
|
}
|
||||||
pu.setDebug(false);
|
pu.setDebug(false);
|
||||||
for (String err : errors)
|
for (String err : errors) {
|
||||||
msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getWebPath(), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR));
|
msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getWebPath(), "Error sorting Differential: "+err, ValidationMessage.IssueSeverity.ERROR));
|
||||||
|
}
|
||||||
pu.generateSnapshot(sd, p, p.getUrl(), sd.getUserString("webroot"), p.getName());
|
pu.generateSnapshot(sd, p, p.getUrl(), sd.getUserString("webroot"), p.getName());
|
||||||
for (ValidationMessage msg : msgs) {
|
for (ValidationMessage msg : msgs) {
|
||||||
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
|
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
|
||||||
|
|
|
@ -941,4 +941,6 @@ public interface IWorkerContext {
|
||||||
|
|
||||||
public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps);
|
public PEBuilder getProfiledElementBuilder(PEElementPropertiesPolicy elementProps, boolean fixedProps);
|
||||||
|
|
||||||
|
public boolean isForPublication();
|
||||||
|
public void setForPublication(boolean value);
|
||||||
}
|
}
|
|
@ -5307,7 +5307,7 @@ public class Bundle extends Resource implements IBaseBundle {
|
||||||
public BundleLinkComponent getLink(String theRelation) {
|
public BundleLinkComponent getLink(String theRelation) {
|
||||||
org.apache.commons.lang3.Validate.notBlank(theRelation, "theRelation may not be null or empty");
|
org.apache.commons.lang3.Validate.notBlank(theRelation, "theRelation may not be null or empty");
|
||||||
for (BundleLinkComponent next : getLink()) {
|
for (BundleLinkComponent next : getLink()) {
|
||||||
if (theRelation.equals(next.getRelation().toCode())) {
|
if (theRelation.equals(next.getRelation().toCode())) {
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -544,7 +544,7 @@ public class Coding extends DataType implements IBaseCoding, ICompositeType, ICo
|
||||||
base = base+"|"+getVersion();
|
base = base+"|"+getVersion();
|
||||||
base = base + "#"+getCode();
|
base = base + "#"+getCode();
|
||||||
if (hasDisplay())
|
if (hasDisplay())
|
||||||
base = base+": "+getDisplay();
|
base = base+": '"+getDisplay()+"'";
|
||||||
return base;
|
return base;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class ResourceUtilities {
|
||||||
|
|
||||||
public static String getLink(Bundle feed, String rel) {
|
public static String getLink(Bundle feed, String rel) {
|
||||||
for (BundleLinkComponent link : feed.getLink()) {
|
for (BundleLinkComponent link : feed.getLink()) {
|
||||||
if (link.getRelation().equals(rel))
|
if (link.getRelation().toCode().equals(rel))
|
||||||
return link.getUrl();
|
return link.getUrl();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1951,6 +1951,30 @@ public class Utilities {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String stripAllPara(String p) {
|
||||||
|
if (noString(p)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
p = p.trim();
|
||||||
|
if (p.startsWith("<p>")) {
|
||||||
|
p = p.substring(3);
|
||||||
|
}
|
||||||
|
if (p.endsWith("</p>")) {
|
||||||
|
p = p.substring(0, p.length()-4);
|
||||||
|
}
|
||||||
|
p = p.replace("</p>", " ");
|
||||||
|
p = p.replace("<p>", "");
|
||||||
|
while (p.contains("<p ")) {
|
||||||
|
int start = p.indexOf("<p ");
|
||||||
|
int end = start;
|
||||||
|
while (end < p.length() && p.charAt(end) != '>') {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
p = p.substring(start, end);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//public static boolean !isWhitespace(String s) {
|
//public static boolean !isWhitespace(String s) {
|
||||||
|
|
|
@ -869,6 +869,7 @@ public class I18nConstants {
|
||||||
public static final String ED_PATH_WRONG_TYPE_MATCH = "ED_PATH_WRONG_TYPE_MATCH";
|
public static final String ED_PATH_WRONG_TYPE_MATCH = "ED_PATH_WRONG_TYPE_MATCH";
|
||||||
public static final String ATTEMPT_TO_CHANGE_SLICING = "ATTEMPT_TO_CHANGE_SLICING";
|
public static final String ATTEMPT_TO_CHANGE_SLICING = "ATTEMPT_TO_CHANGE_SLICING";
|
||||||
public static final String REPEAT_SLICING_IGNORED = "REPEAT_SLICING_IGNORED";
|
public static final String REPEAT_SLICING_IGNORED = "REPEAT_SLICING_IGNORED";
|
||||||
|
public static final String SD_ELEMENT_NOT_IN_CONSTRAINT = "SD_ELEMENT_NOT_IN_CONSTRAINT";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -923,4 +923,4 @@ SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type: extensions should
|
||||||
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
|
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
|
||||||
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
|
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
|
||||||
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored
|
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored
|
||||||
|
SD_ELEMENT_NOT_IN_CONSTRAINT = The element definition for {1} has a property {0} which is not allowed in a profile
|
||||||
|
|
|
@ -5968,14 +5968,30 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (match) {
|
if (match) {
|
||||||
|
boolean update = true;
|
||||||
boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", "")));
|
boolean isOk = ei.definition == null || ei.definition == slicer || (ei.definition.getPath().endsWith("[x]") && ed.getPath().startsWith(ei.definition.getPath().replace("[x]", "")));
|
||||||
|
if (!isOk) {
|
||||||
|
// is this a subslice? then we put it in as a replacement
|
||||||
|
String existingName = ei.definition == null || !ei.definition.hasSliceName() ? null : ei.definition.getSliceName();
|
||||||
|
String matchingName = ed.hasSliceName() ? ed.getSliceName() : null;
|
||||||
|
if (existingName != null && matchingName != null) {
|
||||||
|
if (matchingName.startsWith(existingName+"/")) {
|
||||||
|
isOk = true;
|
||||||
|
} else if (existingName.startsWith(matchingName+"/")) {
|
||||||
|
update = false;
|
||||||
|
isOk = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, I18nConstants.VALIDATION_VAL_PROFILE_MATCHMULTIPLE, profile.getVersionedUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) {
|
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, ei.line(), ei.col(), ei.getPath(), isOk, I18nConstants.VALIDATION_VAL_PROFILE_MATCHMULTIPLE, profile.getVersionedUrl(), (ei.definition == null || !ei.definition.hasSliceName() ? "" : ei.definition.getSliceName()), (ed.hasSliceName() ? ed.getSliceName() : ""))) {
|
||||||
ei.definition = ed;
|
if (update) {
|
||||||
if (ei.slice == null) {
|
ei.definition = ed;
|
||||||
ei.index = i;
|
if (ei.slice == null) {
|
||||||
} else {
|
ei.index = i;
|
||||||
ei.index = sliceOffset;
|
} else {
|
||||||
ei.sliceindex = i - (sliceOffset + 1);
|
ei.index = sliceOffset;
|
||||||
|
ei.sliceindex = i - (sliceOffset + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (childUnsupportedSlicing) {
|
} else if (childUnsupportedSlicing) {
|
||||||
|
|
|
@ -127,14 +127,16 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
|
rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Element> differentials = src.getChildrenByName("differential");
|
List<Element> differentials = src.getChildrenByName("differential");
|
||||||
List<Element> snapshots = src.getChildrenByName("snapshot");
|
List<Element> snapshots = src.getChildrenByName("snapshot");
|
||||||
boolean logical = "logical".equals(src.getNamedChildValue("kind"));
|
boolean logical = "logical".equals(src.getNamedChildValue("kind"));
|
||||||
|
boolean constraint = "constraint".equals(src.getNamedChildValue("derivation"));
|
||||||
for (Element differential : differentials) {
|
for (Element differential : differentials) {
|
||||||
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical) && ok;
|
ok = validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0, sd, typeName, logical, constraint) && ok;
|
||||||
}
|
}
|
||||||
for (Element snapshot : snapshots) {
|
for (Element snapshot : snapshots) {
|
||||||
ok = validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true, sd, typeName, logical) && ok;
|
ok = validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true, sd, typeName, logical, constraint) && ok;
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -174,18 +176,18 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical) {
|
private boolean validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint) {
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
List<Element> elements = elementList.getChildrenByName("element");
|
List<Element> elements = elementList.getChildrenByName("element");
|
||||||
int cc = 0;
|
int cc = 0;
|
||||||
for (Element element : elements) {
|
for (Element element : elements) {
|
||||||
ok = validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical) && ok;
|
ok = validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot, sd, typeName, logical, constraint) && ok;
|
||||||
cc++;
|
cc++;
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical) {
|
private boolean validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot, StructureDefinition sd, String typeName, boolean logical, boolean constraint) {
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
boolean typeMustSupport = false;
|
boolean typeMustSupport = false;
|
||||||
String path = element.getNamedChildValue("path");
|
String path = element.getNamedChildValue("path");
|
||||||
|
@ -194,6 +196,8 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
rule(errors, "2023-01-17", IssueType.INVALID, stack.getLiteralPath(), path.contains(".") || !element.hasChild("slicing"), I18nConstants.SD_NO_SLICING_ON_ROOT, path);
|
rule(errors, "2023-01-17", IssueType.INVALID, stack.getLiteralPath(), path.contains(".") || !element.hasChild("slicing"), I18nConstants.SD_NO_SLICING_ON_ROOT, path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
rule(errors, "2023-05-22", IssueType.NOTFOUND, stack.getLiteralPath(), snapshot || !constraint || !element.hasChild("meaningWhenMissing") || meaningWhenMissingAllowed(element), I18nConstants.SD_ELEMENT_NOT_IN_CONSTRAINT, "meaningWhenMissing", path);
|
||||||
|
|
||||||
List<Element> types = element.getChildrenByName("type");
|
List<Element> types = element.getChildrenByName("type");
|
||||||
Set<String> typeCodes = new HashSet<>();
|
Set<String> typeCodes = new HashSet<>();
|
||||||
Set<String> characteristics = new HashSet<>();
|
Set<String> characteristics = new HashSet<>();
|
||||||
|
@ -312,6 +316,12 @@ public class StructureDefinitionValidator extends BaseValidator {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean meaningWhenMissingAllowed(Element element) {
|
||||||
|
// allowed to use meaningWhenMissing on the root of an element to say what it means when the extension
|
||||||
|
// is not present.
|
||||||
|
return "Extension".equals(element.getPath()) || (element.getPath().endsWith(".extension"));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean addCharacteristics(Set<String> set, String tc) {
|
private boolean addCharacteristics(Set<String> set, String tc) {
|
||||||
switch (tc) {
|
switch (tc) {
|
||||||
case "boolean" : return addCharacteristicsForType(set);
|
case "boolean" : return addCharacteristicsForType(set);
|
||||||
|
|
|
@ -180,7 +180,7 @@ public class SnapShotGenerationXTests {
|
||||||
else
|
else
|
||||||
source = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.xml"));
|
source = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.xml"));
|
||||||
if (!fail)
|
if (!fail)
|
||||||
expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-expected.xml"));
|
expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-output.xml"));
|
||||||
if (!Utilities.noString(include))
|
if (!Utilities.noString(include))
|
||||||
included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml"));
|
included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml"));
|
||||||
if (!Utilities.noString(register)) {
|
if (!Utilities.noString(register)) {
|
||||||
|
@ -471,8 +471,7 @@ public class SnapShotGenerationXTests {
|
||||||
pu.sortDifferential(base, test.getOutput(), test.getOutput().getUrl(), errors, false);
|
pu.sortDifferential(base, test.getOutput(), test.getOutput().getUrl(), errors, false);
|
||||||
if (!errors.isEmpty())
|
if (!errors.isEmpty())
|
||||||
throw new FHIRException(errors.get(0));
|
throw new FHIRException(errors.get(0));
|
||||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected.xml")));
|
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml")), test.getOutput());
|
||||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-actual.xml")), test.getOutput());
|
|
||||||
Assertions.assertTrue(test.expected.equalsDeep(test.output), "Output does not match expected");
|
Assertions.assertTrue(test.expected.equalsDeep(test.output), "Output does not match expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,11 +541,10 @@ public class SnapShotGenerationXTests {
|
||||||
if (!fail) {
|
if (!fail) {
|
||||||
test.output = output;
|
test.output = output;
|
||||||
UtilitiesXTests.context(version).cacheResource(output);
|
UtilitiesXTests.context(version).cacheResource(output);
|
||||||
File dst = new File(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected.xml"));
|
File dst = new File(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml"));
|
||||||
if (dst.exists())
|
if (dst.exists())
|
||||||
dst.delete();
|
dst.delete();
|
||||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(dst));
|
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml")), output);
|
||||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-actual.xml")), output);
|
|
||||||
StructureDefinition t1 = test.expected.copy();
|
StructureDefinition t1 = test.expected.copy();
|
||||||
t1.setText(null);
|
t1.setText(null);
|
||||||
StructureDefinition t2 = test.output.copy();
|
StructureDefinition t2 = test.output.copy();
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static boolean PRINT_OUTPUT_TO_CONSOLE = true;
|
public final static boolean PRINT_OUTPUT_TO_CONSOLE = true;
|
||||||
private static final boolean BUILD_NEW = false;
|
private static final boolean BUILD_NEW = true;
|
||||||
private static final boolean CLONE = true;
|
private static final boolean CLONE = true;
|
||||||
|
|
||||||
@Parameters(name = "{index}: id {0}")
|
@Parameters(name = "{index}: id {0}")
|
||||||
|
|
|
@ -3443,3 +3443,22 @@ v: {
|
||||||
"class" : "SERVER_ERROR"
|
"class" : "SERVER_ERROR"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://loinc.org",
|
||||||
|
"version" : "2.71",
|
||||||
|
"code" : "29463-7",
|
||||||
|
"display" : "Body weight"
|
||||||
|
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "Body weight",
|
||||||
|
"code" : "29463-7",
|
||||||
|
"system" : "http://loinc.org",
|
||||||
|
"version" : "2.74"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -2150,3 +2150,41 @@ v: {
|
||||||
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20210731",
|
||||||
|
"code" : "27113001",
|
||||||
|
"display" : "Body weight (observable entity)"
|
||||||
|
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "Body weight",
|
||||||
|
"code" : "27113001",
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20210731"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20210731",
|
||||||
|
"code" : "38266002",
|
||||||
|
"display" : "Entire body as a whole (body structure)"
|
||||||
|
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"display" : "Entire body as a whole",
|
||||||
|
"code" : "38266002",
|
||||||
|
"system" : "http://snomed.info/sct",
|
||||||
|
"version" : "http://snomed.info/sct/900000000000207008/version/20210731"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -420,3 +420,19 @@ v: {
|
||||||
"version" : "2.0.1"
|
"version" : "2.0.1"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"system" : "http://unitsofmeasure.org",
|
||||||
|
"code" : "KG"
|
||||||
|
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
|
||||||
|
"resourceType" : "Parameters",
|
||||||
|
"parameter" : [{
|
||||||
|
"name" : "profile-url",
|
||||||
|
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
|
||||||
|
}]
|
||||||
|
}}####
|
||||||
|
v: {
|
||||||
|
"severity" : "error",
|
||||||
|
"error" : "Error processing Unit: 'KG': The unit \"KG\" is unknown at character 1; Unknown Code 'KG' in the system 'http://unitsofmeasure.org'; The provided code http://unitsofmeasure.org#KG is not in the value set 'http://hl7.org/fhir/ValueSet/@all' (from Tx-Server)",
|
||||||
|
"class" : "UNKNOWN"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue