Gg v5114 b (#356)

* Ensure "I" flag in profile table representation is not used for underlying infrastructural constraints that exist everywhere

* render multiple values for properties if they exist

* fix for npe

* fix for use of "current" as version

* fix bad package URLs as they are loaded

* RELEASE_NOTES.md

* Add rendering for must support on types, profiles, targets

* Add new validation for must-support on types / profiles / targets + improve extension validation

* add <code> when rendering turtle to HTML

* RELEASE_NOTES.md

* fix notes
This commit is contained in:
Grahame Grieve 2020-09-25 04:06:36 +10:00 committed by GitHub
parent 8eeffb8979
commit b578cfbc0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 20 deletions

View File

@ -1,5 +1,5 @@
Validator:
* no changes with impact
* Add new validation for must-support on types / profiles / targets + improve Extension validation
Other code changes:
* Ensure "I" flag in profile table representation is not used just for infrastructural constraints
@ -7,3 +7,5 @@ Other code changes:
* Fix for npe rendering resources based on profiles
* fix for use of "current" as version
* hack for past bad package URLs
* Add rendering for must support on types, profiles, targets
* add <code> when rendering turtle to HTML

View File

@ -3245,14 +3245,22 @@ public class ProfileUtilities extends TranslatingUtilities {
tl = t;
if (t.hasTarget()) {
c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null));
if (isMustSupportDirect(t) && e.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false);
}
c.getPieces().add(gen.new Piece(null, "(", null));
boolean tfirst = true;
for (UriType u : t.getTargetProfile()) {
for (CanonicalType u : t.getTargetProfile()) {
if (tfirst)
tfirst = false;
else
c.addPiece(gen.new Piece(null, " | ", null));
genTargetLink(gen, profileBaseFileName, corePath, c, t, u.getValue());
if (isMustSupport(u) && e.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false);
}
}
c.getPieces().add(gen.new Piece(null, ")", null));
if (t.getAggregation().size() > 0) {
@ -3289,6 +3297,10 @@ public class ProfileUtilities extends TranslatingUtilities {
}
} else
c.addPiece(checkForNoChange(t, gen.new Piece((p.getValue().startsWith(corePath)? corePath: "")+ref, t.getWorkingCode(), null)));
if (isMustSupport(p) && e.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false);
}
}
} else {
String tc = t.getWorkingCode();
@ -3301,8 +3313,13 @@ public class ProfileUtilities extends TranslatingUtilities {
}
} else if (pkp != null && pkp.hasLinkFor(tc)) {
c.addPiece(checkForNoChange(t, gen.new Piece(pkp.getLinkFor(corePath, tc), tc, null)));
} else
} else {
c.addPiece(checkForNoChange(t, gen.new Piece(null, tc, null)));
}
if (isMustSupportDirect(t) && e.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false);
}
}
}
return c;
@ -3931,6 +3948,10 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(gen.new Piece(corePath+"datatypes.html#canonical", "canonical", null));
else
c.getPieces().add(gen.new Piece(corePath+"references.html#Reference", "Reference", null));
if (isMustSupportDirect(tr) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false);
}
c.getPieces().add(gen.new Piece(null, "(", null));
}
boolean first = true;
@ -3938,6 +3959,10 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!first)
c.getPieces().add(gen.new Piece(null, " | ", null));
genTargetLink(gen, profileBaseFileName, corePath, c, tr, rt.getValue());
if (isMustSupport(rt) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This target must be supported"), "S", "white", "red", null, false);
}
first = false;
}
if (first)
@ -3956,20 +3981,23 @@ public class ProfileUtilities extends TranslatingUtilities {
choicerow.getCells().add(gen.new Cell());
choicerow.getCells().add(gen.new Cell(null, null, "", null, null));
choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE);
choicerow.getCells().add(gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null));
// } else if (definitions.getConstraints().contthnsKey(t)) {
// ProfiledType pt = definitions.getConstraints().get(t);
// choicerow.getCells().add(gen.new Cell(null, null, e.getName().replace("[x]", Utilities.capitalize(pt.getBaseType())), definitions.getTypes().containsKey(t) ? definitions.getTypes().get(t).getDefinition() : null, null));
// choicerow.getCells().add(gen.new Cell());
// choicerow.getCells().add(gen.new Cell(null, null, "", null, null));
// choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE);
// choicerow.getCells().add(gen.new Cell(null, definitions.getSrcFile(t)+".html#"+t.replace("*", "open"), t, null, null));
Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getType(), null, null);
choicerow.getCells().add(c);
if (isMustSupport(tr) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false);
}
} else {
choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null));
choicerow.getCells().add(gen.new Cell());
choicerow.getCells().add(gen.new Cell(null, null, "", null, null));
choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE);
choicerow.getCells().add(gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null));
Cell c = gen.new Cell(null, pkp.getLinkFor(corePath, t), sd.getType(), null, null);
choicerow.getCells().add(c);
if (isMustSupport(tr) && element.getMustSupport()) {
c.addPiece(gen.new Piece(null, " ", null));
c.addStyledText(translate("sd.table", "This type must be supported"), "S", "white", "red", null, false);
}
}
if (tr.hasProfile()) {
Cell typeCell = choicerow.getCells().get(3);
@ -3982,6 +4010,10 @@ public class ProfileUtilities extends TranslatingUtilities {
typeCell.addPiece(gen.new Piece(null, "?gen-e2?", null));
else
typeCell.addPiece(gen.new Piece(psd.getUserString("path"), psd.getName(), psd.present()));
if (isMustSupport(pt) && element.getMustSupport()) {
typeCell.addPiece(gen.new Piece(null, " ", null));
typeCell.addStyledText(translate("sd.table", "This profile must be supported"), "S", "white", "red", null, false);
}
}
typeCell.addPiece(gen.new Piece(null, ")", null));
@ -6177,6 +6209,32 @@ public class ProfileUtilities extends TranslatingUtilities {
return grp;
}
public static boolean isMustSupportDirect(TypeRefComponent tr) {
return ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT)));
}
public static boolean isMustSupport(TypeRefComponent tr) {
if ("true".equals(ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
if (isMustSupport(tr.getProfile())) {
return true;
}
return isMustSupport(tr.getTargetProfile());
}
public static boolean isMustSupport(List<CanonicalType> profiles) {
for (CanonicalType ct : profiles) {
if (isMustSupport(ct)) {
return true;
}
}
return false;
}
public static boolean isMustSupport(CanonicalType profile) {
return "true".equals(ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_MUST_SUPPORT));
}
}

View File

@ -180,6 +180,7 @@ public class ToolingExtensions {
public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence";
public static final String EXT_EXP_FRAGMENT = "http://hl7.org/fhir/tools/StructureDefinition/expansion-codesystem-fragment";
public static final String EXT_EXP_TOOCOSTLY = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly";
public static final String EXT_MUST_SUPPORT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support";
// specific extension helpers
@ -335,6 +336,8 @@ public class ToolingExtensions {
return ((DecimalType) ex.getValue()).asStringValue();
if ((ex.getValue() instanceof MarkdownType))
return ((MarkdownType) ex.getValue()).getValue();
if ((ex.getValue() instanceof PrimitiveType))
return ((PrimitiveType) ex.getValue()).primitiveValue();
if (!(ex.getValue() instanceof StringType))
return null;
return ((StringType) ex.getValue()).getValue();

View File

@ -341,6 +341,8 @@ public class I18nConstants {
public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___";
public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_";
public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION";
public static final String SD_NESTED_MUST_SUPPORT_DIFF = "SD_NESTED_MUST_SUPPORT_DIFF";
public static final String SD_NESTED_MUST_SUPPORT_SNAPSHOT = "SD_NESTED_MUST_SUPPORT_SNAPSHOT";
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";
public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND";

View File

@ -384,12 +384,12 @@ public class Turtle {
public String asHtml() throws Exception {
StringBuilder b = new StringBuilder();
b.append("<pre class=\"rdf\">\r\n");
b.append("<pre class=\"rdf\"><code class=\"language-turtle\">\r\n");
commitPrefixes(b);
for (Section s : sections) {
commitSection(b, s);
}
b.append("</pre>\r\n");
b.append("</code></pre>\r\n");
b.append("\r\n");
return b.toString();
}

View File

@ -609,3 +609,6 @@ TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = UCUM Codes that contain human reada
XHTML_XHTML_ELEMENT_ILLEGAL_IN_PARA = Illegal 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}
SD_NESTED_MUST_SUPPORT_DIFF = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support. The inner must-supports will be ignored unless the element inherits must-support = true
SD_NESTED_MUST_SUPPORT_SNAPSHOT = The element {0} has types/profiles/targets that are marked as must support, but the element itself is not marked as must-support

View File

@ -296,9 +296,10 @@ public class BaseValidator {
* Set this parameter to <code>false</code> if the validation does not pass
* @return Returns <code>thePass</code> (in other words, returns <code>true</code> if the rule did not fail validation)
*/
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String msg) {
protected boolean hint(List<ValidationMessage> errors, IssueType type, String path, boolean thePass, String theMessage, Object... theMessageArguments) {
if (!thePass) {
addValidationMessage(errors, type, -1, -1, path, msg, IssueSeverity.INFORMATION, null);
String message = context.formatMessage(theMessage, theMessageArguments);
addValidationMessage(errors, type, -1, -1, path, message, IssueSeverity.INFORMATION, null);
}
return thePass;
}

View File

@ -483,7 +483,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean isKnownExtension(String url) {
// Added structuredefinition-expression and following extensions explicitly because they weren't defined in the version of the spec they need to be used with
if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression") || url.equals(VersionConvertorConstants.IG_DEPENDSON_PACKAGE_EXTENSION))
if ((allowExamples && (url.contains("example.org") || url.contains("acme.com"))) || url.contains("nema.org") || url.startsWith("http://hl7.org/fhir/tools/StructureDefinition/") || url.equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-expression"))
return true;
for (String s : extensionDomains)
if (url.startsWith(s))
@ -1521,6 +1521,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (extensionUrl.equals(profile.getUrl())) {
rule(errors, IssueType.INVALID, element.line(), element.col(), path + "[url='" + url + "']", hasExtensionSlice(profile, url), I18nConstants.EXTENSION_EXT_SUBEXTENSION_INVALID, url, profile.getUrl());
}
} else if (SpecialExtensions.isKnownExtension(url)) {
ex = SpecialExtensions.getDefinition(url);
} else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN_NOTHERE, url)) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN, url);
}
@ -1556,6 +1558,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ex;
}
private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) {
for (ElementDefinition ed : profile.getSnapshot().getElement()) {
if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) {
@ -1933,7 +1936,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (fetcher != null) {
boolean found;
try {
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url);
found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) || fetcher.resolveURL(appContext, path, url) ||
SpecialExtensions.isKnownExtension(url);
} catch (IOException e1) {
found = false;
}

File diff suppressed because one or more lines are too long

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.SearchParameter;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
@ -95,6 +96,69 @@ public class StructureDefinitionValidator extends BaseValidator {
} catch (FHIRException | IOException e) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
}
List<Element> differentials = src.getChildrenByName("differential");
List<Element> snapshots = src.getChildrenByName("snapshot");
for (Element differential : differentials) {
validateElementList(errors, differential, stack.push(differential, -1, null, null), false, snapshots.size() > 0);
}
for (Element snapshot : snapshots) {
validateElementList(errors, snapshot, stack.push(snapshot, -1, null, null), true, true);
}
}
private void validateElementList(List<ValidationMessage> errors, Element elementList, NodeStack stack, boolean snapshot, boolean hasSnapshot) {
List<Element> elements = elementList.getChildrenByName("element");
int cc = 0;
for (Element element : elements) {
validateElementDefinition(errors, element, stack.push(element, cc, null, null), snapshot, hasSnapshot);
cc++;
}
}
private void validateElementDefinition(List<ValidationMessage> errors, Element element, NodeStack stack, boolean snapshot, boolean hasSnapshot) {
boolean typeMustSupport = false;
List<Element> types = element.getChildrenByName("type");
for (Element type : types) {
if (hasMustSupportExtension(type)) {
typeMustSupport = true;
}
}
if (typeMustSupport) {
if (snapshot) {
rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_SNAPSHOT, element.getNamedChildValue("path"));
} else {
hint(errors, IssueType.EXCEPTION, stack.getLiteralPath(), hasSnapshot || "true".equals(element.getChildValue("mustSupport")), I18nConstants.SD_NESTED_MUST_SUPPORT_DIFF, element.getNamedChildValue("path"));
}
}
}
private boolean hasMustSupportExtension(Element type) {
if ("true".equals(getExtensionValue(type, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
List<Element> profiles = type.getChildrenByName("profile");
for (Element profile : profiles) {
if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
}
profiles = type.getChildrenByName("targetProfile");
for (Element profile : profiles) {
if ("true".equals(getExtensionValue(profile, ToolingExtensions.EXT_MUST_SUPPORT))) {
return true;
}
}
return false;
}
private String getExtensionValue(Element element, String url) {
List<Element> extensions = element.getChildrenByName("extension");
for (Element extension : extensions) {
if (url.equals(extension.getNamedChildValue("url"))) {
return extension.getNamedChildValue("value");
}
}
return null;
}
private StructureDefinition loadAsSD(Element src) throws FHIRException, IOException {