use of org.hl7.fhir.utilities.validation.ValidationMessage in project org.hl7.fhir.core by hapifhir.
the class OperationOutcomeUtilities method convertToIssue.
public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) {
OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
issue.setUserData("source.vm", message);
issue.setCode(convert(message.getType()));
if (message.getLocation() != null) {
// message location has a fhirPath in it. We need to populate the expression
issue.addExpression(message.getLocation());
}
// pass through line/col if they're present
if (message.getLine() >= 0)
issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_LINE).setValue(new IntegerType(message.getLine()));
if (message.getCol() >= 0)
issue.addExtension().setUrl(ToolingExtensions.EXT_ISSUE_COL).setValue(new IntegerType(message.getCol()));
issue.setSeverity(convert(message.getLevel()));
CodeableConcept c = new CodeableConcept();
c.setText(message.getMessage());
issue.setDetails(c);
if (message.getSource() != null) {
issue.getExtension().add(ToolingExtensions.makeIssueSource(message.getSource()));
}
issue.setUserData("source.msg", message);
return issue;
}
use of org.hl7.fhir.utilities.validation.ValidationMessage in project org.hl7.fhir.core by hapifhir.
the class ToolingExtensions method readValidationMessage.
public static ValidationMessage readValidationMessage(OperationOutcomeIssueComponent issue, Source source) {
ValidationMessage vm = new ValidationMessage();
vm.setSource(source);
vm.setLevel(mapSeverity(issue.getSeverity()));
vm.setType(mapType(issue.getCode()));
if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_LINE))
vm.setLine(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, 0));
if (issue.hasExtension(ToolingExtensions.EXT_ISSUE_COL))
vm.setCol(ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, 0));
if (issue.hasExpression())
vm.setLocation(issue.getExpression().get(0).asStringValue());
vm.setMessage(issue.getDetails().getText());
if (issue.hasExtension("http://hl7.org/fhir/StructureDefinition/rendering-xhtml"))
vm.setHtml(ToolingExtensions.readStringExtension(issue, "http://hl7.org/fhir/StructureDefinition/rendering-xhtml"));
return vm;
}
use of org.hl7.fhir.utilities.validation.ValidationMessage in project org.hl7.fhir.core by hapifhir.
the class ProfileUtilities method generateSnapshot.
/**
* Given a base (snapshot) profile structure, and a differential profile, generate a new snapshot profile
*
* @param base - the base structure on which the differential will be applied
* @param differential - the differential to apply to the base
* @param url - where the base has relative urls for profile references, these need to be converted to absolutes by prepending this URL (e.g. the canonical URL)
* @param webUrl - where the base has relative urls in markdown, these need to be converted to absolutes by prepending this URL (this is not the same as the canonical URL)
* @param trimDifferential - if this is true, then the snap short generator will remove any material in the element definitions that is not different to the base
* @return
* @throws FHIRException
* @throws DefinitionException
* @throws Exception
*/
public void generateSnapshot(StructureDefinition base, StructureDefinition derived, String url, String webUrl, String profileName) throws DefinitionException, FHIRException {
if (base == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.NO_BASE_PROFILE_PROVIDED));
}
if (derived == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.NO_DERIVED_STRUCTURE_PROVIDED));
}
checkNotGenerating(base, "Base for generating a snapshot for the profile " + derived.getUrl());
checkNotGenerating(derived, "Focus for generating a snapshot");
if (!base.hasType()) {
throw new DefinitionException(context.formatMessage(I18nConstants.BASE_PROFILE__HAS_NO_TYPE, base.getUrl()));
}
if (!derived.hasType()) {
throw new DefinitionException(context.formatMessage(I18nConstants.DERIVED_PROFILE__HAS_NO_TYPE, derived.getUrl()));
}
if (!derived.hasDerivation()) {
throw new DefinitionException(context.formatMessage(I18nConstants.DERIVED_PROFILE__HAS_NO_DERIVATION_VALUE_AND_SO_CANT_BE_PROCESSED, derived.getUrl()));
}
if (!base.getType().equals(derived.getType()) && derived.getDerivation() == TypeDerivationRule.CONSTRAINT) {
throw new DefinitionException(context.formatMessage(I18nConstants.BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___, base.getUrl(), base.getType(), derived.getUrl(), derived.getType()));
}
if (snapshotStack.contains(derived.getUrl())) {
throw new DefinitionException(context.formatMessage(I18nConstants.CIRCULAR_SNAPSHOT_REFERENCES_DETECTED_CANNOT_GENERATE_SNAPSHOT_STACK__, snapshotStack.toString()));
}
derived.setUserData("profileutils.snapshot.generating", true);
snapshotStack.add(derived.getUrl());
try {
if (!Utilities.noString(webUrl) && !webUrl.endsWith("/"))
webUrl = webUrl + '/';
if (defWebRoot == null)
defWebRoot = webUrl;
derived.setSnapshot(new StructureDefinitionSnapshotComponent());
try {
checkDifferential(derived.getDifferential().getElement(), typeName(derived.getType()), derived.getUrl());
checkDifferentialBaseType(derived);
// so we have two lists - the base list, and the differential list
// the differential list is only allowed to include things that are in the base list, but
// is allowed to include them multiple times - thereby slicing them
// our approach is to walk through the base list, and see whether the differential
// says anything about them.
int baseCursor = 0;
// we need a diff cursor because we can only look ahead, in the bound scoped by longer paths
int diffCursor = 0;
for (ElementDefinition e : derived.getDifferential().getElement()) e.clearUserData(GENERATED_IN_SNAPSHOT);
// we actually delegate the work to a subroutine so we can re-enter it with a different cursors
// we make a copy here because we're sometimes going to hack the differential while processing it. Have to migrate user data back afterwards
StructureDefinitionDifferentialComponent diff = cloneDiff(derived.getDifferential());
StructureDefinitionSnapshotComponent baseSnapshot = base.getSnapshot();
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
String derivedType = derived.getType();
if (StructureDefinitionKind.LOGICAL.equals(derived.getKind()) && derived.getType().contains("/")) {
derivedType = derivedType.substring(derivedType.lastIndexOf("/") + 1);
}
baseSnapshot = cloneSnapshot(baseSnapshot, base.getType(), derivedType);
}
// if (derived.getId().equals("2.16.840.1.113883.10.20.22.2.1.1")) {
// debug = true;
// }
processPaths("", derived.getSnapshot(), baseSnapshot, diff, baseCursor, diffCursor, baseSnapshot.getElement().size() - 1, derived.getDifferential().hasElement() ? derived.getDifferential().getElement().size() - 1 : -1, url, webUrl, derived.present(), null, null, false, base.getUrl(), null, false, null, null, new ArrayList<ElementRedirection>(), base);
checkGroupConstraints(derived);
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
ElementDefinition outcome = updateURLs(url, webUrl, e.copy());
e.setUserData(GENERATED_IN_SNAPSHOT, outcome);
derived.getSnapshot().addElement(outcome);
}
}
}
if (derived.getKind() != StructureDefinitionKind.LOGICAL && !derived.getSnapshot().getElementFirstRep().getType().isEmpty())
throw new Error(context.formatMessage(I18nConstants.TYPE_ON_FIRST_SNAPSHOT_ELEMENT_FOR__IN__FROM_, derived.getSnapshot().getElementFirstRep().getPath(), derived.getUrl(), base.getUrl()));
updateMaps(base, derived);
setIds(derived, false);
if (debug) {
System.out.println("Differential: ");
for (ElementDefinition ed : derived.getDifferential().getElement()) System.out.println(" " + ed.getId() + " : " + typeSummaryWithProfile(ed) + "[" + ed.getMin() + ".." + ed.getMax() + "]" + sliceSummary(ed) + " " + constraintSummary(ed));
System.out.println("Snapshot: ");
for (ElementDefinition ed : derived.getSnapshot().getElement()) System.out.println(" " + ed.getId() + " : " + typeSummaryWithProfile(ed) + "[" + ed.getMin() + ".." + ed.getMax() + "]" + sliceSummary(ed) + " " + constraintSummary(ed));
}
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
// Check that all differential elements have a corresponding snapshot element
int ce = 0;
for (ElementDefinition e : diff.getElement()) {
if (!e.hasUserData("diff-source"))
throw new Error(context.formatMessage(I18nConstants.UNXPECTED_INTERNAL_CONDITION__NO_SOURCE_ON_DIFF_ELEMENT));
else {
if (e.hasUserData(DERIVATION_EQUALS))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_EQUALS, e.getUserData(DERIVATION_EQUALS));
if (e.hasUserData(DERIVATION_POINTER))
((Base) e.getUserData("diff-source")).setUserData(DERIVATION_POINTER, e.getUserData(DERIVATION_POINTER));
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
b.append(e.hasId() ? "id: " + e.getId() : "path: " + e.getPath());
ce++;
if (e.hasId()) {
String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url + "#" + e.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
}
}
if (!Utilities.noString(b.toString())) {
String msg = "The profile " + derived.getUrl() + " has " + ce + " " + Utilities.pluralize("element", ce) + " in the differential (" + b.toString() + ") that don't have a matching element in the snapshot: check that the path and definitions are legal in the differential (including order)";
System.out.println("Error in snapshot generation: " + msg);
if (!debug) {
System.out.println("Differential: ");
for (ElementDefinition ed : derived.getDifferential().getElement()) System.out.println(" " + ed.getId() + " = " + ed.getPath() + " : " + typeSummaryWithProfile(ed) + "[" + ed.getMin() + ".." + ed.getMax() + "]" + sliceSummary(ed) + " " + constraintSummary(ed));
System.out.println("Snapshot: ");
for (ElementDefinition ed : derived.getSnapshot().getElement()) System.out.println(" " + ed.getId() + " = " + ed.getPath() + " : " + typeSummaryWithProfile(ed) + "[" + ed.getMin() + ".." + ed.getMax() + "]" + sliceSummary(ed) + " " + constraintSummary(ed));
}
if (exception)
throw new DefinitionException(msg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url, msg, ValidationMessage.IssueSeverity.ERROR));
}
// hack around a problem in R4 definitions (somewhere?)
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (ElementDefinitionMappingComponent mm : ed.getMapping()) {
if (mm.hasMap()) {
mm.setMap(mm.getMap().trim());
}
}
for (ElementDefinitionConstraintComponent s : ed.getConstraint()) {
if (s.hasSource()) {
String ref = s.getSource();
if (!Utilities.isAbsoluteUrl(ref)) {
if (ref.contains(".")) {
s.setSource("http://hl7.org/fhir/StructureDefinition/" + ref.substring(0, ref.indexOf(".")) + "#" + ref);
} else {
s.setSource("http://hl7.org/fhir/StructureDefinition/" + ref);
}
}
}
}
}
if (derived.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
if (!ed.hasBase()) {
ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax());
}
}
}
// last, check for wrong profiles or target profiles
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (TypeRefComponent t : ed.getType()) {
for (UriType u : t.getProfile()) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
if (sd == null) {
if (xver != null && xver.matchingUrl(u.getValue()) && xver.status(u.getValue()) == XVerExtensionStatus.Valid) {
sd = xver.makeDefinition(u.getValue());
}
}
if (sd == null) {
if (messages != null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url + "#" + ed.getId(), "The type of profile " + u.getValue() + " cannot be checked as the profile is not known", IssueSeverity.WARNING));
}
} else {
String wt = t.getWorkingCode();
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
wt = "OperationOutcome";
}
if (!sd.getType().equals(wt)) {
boolean ok = isCompatibleType(wt, sd);
if (!ok) {
String smsg = "The profile " + u.getValue() + " has type " + sd.getType() + " which is not consistent with the stated type " + wt;
if (exception)
throw new DefinitionException(smsg);
else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url + "#" + ed.getId(), smsg, IssueSeverity.ERROR));
}
}
}
}
}
}
} catch (Exception e) {
// if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind
derived.setSnapshot(null);
derived.clearUserData("profileutils.snapshot.generating");
throw e;
}
} finally {
derived.clearUserData("profileutils.snapshot.generating");
snapshotStack.remove(derived.getUrl());
}
}
use of org.hl7.fhir.utilities.validation.ValidationMessage in project org.hl7.fhir.core by hapifhir.
the class ProfileUtilities method generateIds.
private void generateIds(List<ElementDefinition> list, String name, String type) throws DefinitionException {
if (list.isEmpty())
return;
Map<String, String> idList = new HashMap<String, String>();
Map<String, String> replacedIds = new HashMap<String, String>();
SliceList sliceInfo = new SliceList();
// first pass, update the element ids
for (ElementDefinition ed : list) {
List<String> paths = new ArrayList<String>();
if (!ed.hasPath())
throw new DefinitionException(context.formatMessage(I18nConstants.NO_PATH_ON_ELEMENT_DEFINITION__IN_, Integer.toString(list.indexOf(ed)), name));
sliceInfo.seeElement(ed);
String[] pl = ed.getPath().split("\\.");
for (// -1 because the last path is in focus
int i = paths.size(); // -1 because the last path is in focus
i < pl.length; // -1 because the last path is in focus
i++) paths.add(pl[i]);
String[] slices = sliceInfo.analyse(paths);
StringBuilder b = new StringBuilder();
b.append(paths.get(0));
for (int i = 1; i < paths.size(); i++) {
b.append(".");
String s = paths.get(i);
String p = slices[i];
b.append(fixChars(s));
if (p != null) {
b.append(":");
b.append(p);
}
}
String bs = b.toString();
if (ed.hasId()) {
replacedIds.put(ed.getId(), ed.getPath());
}
ed.setId(bs);
if (idList.containsKey(bs)) {
if (exception || messages == null) {
throw new DefinitionException(context.formatMessage(I18nConstants.SAME_ID_ON_MULTIPLE_ELEMENTS__IN_, bs, idList.get(bs), ed.getPath(), name));
} else
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, name + "." + bs, "Duplicate Element id " + bs, ValidationMessage.IssueSeverity.ERROR));
}
idList.put(bs, ed.getPath());
if (ed.hasContentReference() && ed.getContentReference().startsWith("#")) {
String s = ed.getContentReference();
if (replacedIds.containsKey(s.substring(1))) {
ed.setContentReference("http://hl7.org/fhir/StructureDefinition/" + type + "#" + replacedIds.get(s.substring(1)));
} else {
ed.setContentReference("http://hl7.org/fhir/StructureDefinition/" + type + s);
}
}
}
// second path - fix up any broken path based id references
}
use of org.hl7.fhir.utilities.validation.ValidationMessage in project org.hl7.fhir.core by hapifhir.
the class SimpleWorkerContext method generateSnapshot.
@Override
public void generateSnapshot(StructureDefinition p, boolean logical) throws FHIRException {
if ((!p.hasSnapshot() || isProfileNeedsRegenerate(p)) && (logical || p.getKind() != StructureDefinitionKind.LOGICAL)) {
if (!p.hasBaseDefinition())
throw new DefinitionException(formatMessage(I18nConstants.PROFILE___HAS_NO_BASE_AND_NO_SNAPSHOT, p.getName(), p.getUrl()));
StructureDefinition sd = fetchResource(StructureDefinition.class, p.getBaseDefinition());
if (sd == null && "http://hl7.org/fhir/StructureDefinition/Base".equals(p.getBaseDefinition())) {
sd = ProfileUtilities.makeBaseDefinition(p.getFhirVersion());
}
if (sd == null) {
throw new DefinitionException(formatMessage(I18nConstants.PROFILE___BASE__COULD_NOT_BE_RESOLVED, p.getName(), p.getUrl(), p.getBaseDefinition()));
}
List<ValidationMessage> msgs = new ArrayList<ValidationMessage>();
List<String> errors = new ArrayList<String>();
ProfileUtilities pu = new ProfileUtilities(this, msgs, this);
pu.setAutoFixSliceNames(true);
pu.setThrowException(false);
if (xverManager == null) {
xverManager = new XVerExtensionManager(this);
}
pu.setXver(xverManager);
if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
pu.sortDifferential(sd, p, p.getUrl(), errors, true);
}
pu.setDebug(false);
for (String err : errors) msgs.add(new ValidationMessage(Source.ProfileValidator, IssueType.EXCEPTION, p.getUserString("path"), "Error sorting Differential: " + err, ValidationMessage.IssueSeverity.ERROR));
pu.generateSnapshot(sd, p, p.getUrl(), sd.getUserString("webroot"), p.getName());
for (ValidationMessage msg : msgs) {
if ((!ignoreProfileErrors && msg.getLevel() == ValidationMessage.IssueSeverity.ERROR) || msg.getLevel() == ValidationMessage.IssueSeverity.FATAL)
throw new DefinitionException(formatMessage(I18nConstants.PROFILE___ELEMENT__ERROR_GENERATING_SNAPSHOT_, p.getName(), p.getUrl(), msg.getLocation(), msg.getMessage()));
}
if (!p.hasSnapshot())
throw new FHIRException(formatMessage(I18nConstants.PROFILE___ERROR_GENERATING_SNAPSHOT, p.getName(), p.getUrl()));
pu = null;
}
}
Aggregations