Search in sources :

Example 61 with ElementDefn

use of org.hl7.fhir.definitions.model.ElementDefn in project kindling by HL7.

the class SchematronGenerator method genChildren.

private void genChildren(Section section, String path, String typeCode, ElementDefn ed, Definitions definitions, List<String> parents) throws Exception {
    if (!path.contains("//")) {
        ArrayList<String> l = new ArrayList<String>(parents);
        l.add(typeCode);
        for (ElementDefn cd : ed.getElements()) {
            if (!Utilities.noString(cd.typeCode()) && l.contains(cd.typeCode())) {
                // well, we've recursed. What's going to happen now is that we're going to write this as // because we're going to keep recursing.
                // the next call will write this rule, and then terminate
                generateInvariants(section, path + "/", cd, definitions, l, cd.getName());
            } else
                generateInvariants(section, path, cd, definitions, l, cd.getName());
        }
    }
}
Also used : ArrayList(java.util.ArrayList) ElementDefn(org.hl7.fhir.definitions.model.ElementDefn)

Example 62 with ElementDefn

use of org.hl7.fhir.definitions.model.ElementDefn in project kindling by HL7.

the class DataTypeTableGenerator method generate.

public XhtmlNode generate(ElementDefn e, Set<String> outputTracker, boolean isActive) throws Exception {
    HierarchicalTableGenerator gen = new HierarchicalTableGenerator(dest, inlineGraphics, false);
    TableModel model = gen.initNormalTable("", false, true, e.getName(), isActive);
    model.getRows().add(genElement(e, gen, false, e.getName(), false, "", RenderMode.DATATYPE, true, e.getStandardsStatus(), e.isAbstractType(), false));
    return gen.generate(model, "", 0, outputTracker);
}
Also used : HierarchicalTableGenerator(org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator) TableModel(org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel)

Example 63 with ElementDefn

use of org.hl7.fhir.definitions.model.ElementDefn in project kindling by HL7.

the class ResourceValidator method check.

// private List<ValidationMessage> check(String n, BindingSpecification cd) throws Exception {
// List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
// check(errors, n, cd);
// return errors;
// }
private void check(List<ValidationMessage> errors, String path, BindingSpecification cd, String sd, ElementDefn e) throws Exception {
    // basic integrity checks
    List<DefinedCode> ac = cd.getAllCodes(definitions.getCodeSystems(), definitions.getValuesets(), false);
    for (DefinedCode c : ac) {
        String d = c.getCode();
        if (Utilities.noString(d))
            d = c.getId();
        if (Utilities.noString(d))
            d = c.getDisplay();
        if (Utilities.noString(d))
            d = c.getDisplay();
        if (Utilities.noString(c.getSystem()))
            warning(errors, IssueType.STRUCTURE, "Binding @ " + path, !Utilities.noString(c.getDefinition()), "Code " + d + " must have a definition");
        rule(errors, IssueType.STRUCTURE, "Binding @ " + path, !(Utilities.noString(c.getId()) && Utilities.noString(c.getSystem())), "Code " + d + " must have a id or a system");
    }
    // trigger processing into a Heirachical set if necessary
    // rule(errors, IssueType.STRUCTURE, "Binding @ "+path, !cd.isHeirachical() || (cd.getChildCodes().size() < cd.getCodes().size()), "Logic error processing Hirachical code set");
    // now, rules for the source
    hint(errors, IssueType.STRUCTURE, "Binding @ " + path, cd.getBinding() != BindingMethod.Unbound, "Need to provide a binding");
    rule(errors, IssueType.STRUCTURE, "Binding @ " + path, Utilities.noString(cd.getDefinition()) || (cd.getDefinition().charAt(0) == cd.getDefinition().toUpperCase().charAt(0)), "Definition cannot start with a lowercase letter");
    if (cd.getBinding() == BindingMethod.CodeList || (cd.getBinding() == BindingMethod.ValueSet && cd.getStrength() == BindingStrength.REQUIRED && ac.size() > 0 && "code".equals(e.typeCode()))) {
        if (path.toLowerCase().endsWith("status")) {
            if (rule(errors, IssueType.STRUCTURE, path, definitions.getStatusCodes().containsKey(path), "Status element not registered in status-codes.xml")) {
                // rule(errors, IssueType.STRUCTURE, path, e.isModifier(), "Status elements that map to status-codes should be labelled as a modifier");
                ArrayList<String> list = definitions.getStatusCodes().get(path);
                for (DefinedCode c : ac) {
                    boolean ok = false;
                    for (String s : list) {
                        String[] parts = s.split("\\,");
                        for (String p : parts) if (p.trim().equals(c.getCode()))
                            ok = true;
                    }
                    rule(errors, IssueType.STRUCTURE, path, ok, "Status element code \"" + c.getCode() + "\" not found in status-codes.xml");
                }
                for (String s : list) {
                    String[] parts = s.split("\\,");
                    for (String p : parts) {
                        List<String> cl = new ArrayList<>();
                        if (!Utilities.noString(p)) {
                            boolean ok = false;
                            for (DefinedCode c : ac) {
                                cl.add(c.getCode());
                                if (p.trim().equals(c.getCode()))
                                    ok = true;
                            }
                            if (!ok)
                                rule(errors, IssueType.STRUCTURE, path, ok, "Status element code \"" + p + "\" found for " + path + " in status-codes.xml but has no matching code in the resource (codes = " + cl + ")");
                        }
                    }
                }
            }
        }
        StringBuilder b = new StringBuilder();
        for (DefinedCode c : ac) {
            if (!c.getAbstract())
                b.append(" | ").append(c.getCode());
        }
        if (sd.equals("*")) {
            e.setShortDefn(b.toString().substring(3));
            sd = b.toString().substring(3);
        }
        if (sd.contains("|")) {
            if (b.length() < 3)
                throw new Error("surprise");
            String esd = b.substring(3);
            rule(errors, IssueType.STRUCTURE, path, sd.startsWith(esd) || (sd.endsWith("+") && b.substring(3).startsWith(sd.substring(0, sd.length() - 1))), "The short description \"" + sd + "\" does not match the expected (\"" + b.substring(3) + "\")");
        } else {
            rule(errors, IssueType.STRUCTURE, path, cd.getStrength() != BindingStrength.REQUIRED || ac.size() > 12 || ac.size() <= 1 || !hasGoodCode(ac) || isExemptFromCodeList(path), "The short description of an element with a code list should have the format code | code | etc (is " + sd.toString() + ") (" + ac.size() + " codes = \"" + b.toString() + "\")");
        }
    }
    boolean isComplex = !e.typeCode().equals("code");
    if (isComplex && cd.getValueSet() != null && hasInternalReference(cd.getValueSet()) && cd.getStrength() != BindingStrength.EXAMPLE) {
        hint(errors, IssueType.BUSINESSRULE, path, false, "The value " + cd.getValueSet().getUrl() + " defines codes, but is used by a Coding/CodeableConcept @ " + path + ", so it should not use FHIR defined codes");
        cd.getValueSet().setUserData("vs-val-warned", true);
    }
    if (cd.getElementType() == ElementType.Unknown) {
        if (isComplex)
            cd.setElementType(ElementType.Complex);
        else
            cd.setElementType(ElementType.Simple);
    } else if (isComplex && !cd.hasMax())
        rule(errors, IssueType.STRUCTURE, path, cd.getElementType() == ElementType.Complex, "Cannot use a binding from both code and Coding/CodeableConcept elements");
    else
        rule(errors, IssueType.STRUCTURE, path, cd.getElementType() == ElementType.Simple, "Cannot use a binding from both code and Coding/CodeableConcept elements");
    if (isComplex && cd.getValueSet() != null) {
        for (ConceptSetComponent inc : cd.getValueSet().getCompose().getInclude()) if (inc.hasSystem())
            txurls.add(inc.getSystem());
    }
    rule(errors, IssueType.STRUCTURE, "Binding @ " + path, (cd.getElementType() != ElementType.Simple || cd.getStrength() == BindingStrength.REQUIRED || cd.hasMax()) || isExemptFromProperBindingRules(path), "Must be a required binding if bound to a code instead of a Coding/CodeableConcept");
    rule(errors, IssueType.STRUCTURE, "Binding @ " + path, cd.getElementType() != ElementType.Simple || cd.getBinding() != BindingMethod.Unbound, "Need to provide a binding for code elements");
    if (!isComplex && !externalException(path)) {
        ValueSet vs = cd.getValueSet();
        if (warning(errors, IssueType.REQUIRED, path, vs != null || cd.hasReference(), "Unable to resolve value set on 'code' Binding")) {
            hint(errors, IssueType.REQUIRED, path, noExternals(vs), "Bindings for code data types should only use internally defined codes (" + vs.getUrl() + ")");
        // don't disable this without discussion on Zulip
        }
    }
}
Also used : ConceptSetComponent(org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent) ValueSet(org.hl7.fhir.r5.model.ValueSet)

Example 64 with ElementDefn

use of org.hl7.fhir.definitions.model.ElementDefn in project kindling by HL7.

the class ResourceValidator method check.

public void check(List<ValidationMessage> errors, String name, ResourceDefn rd) throws Exception {
    for (String s : rd.getHints()) hint(errors, IssueType.INFORMATIONAL, rd.getName(), false, s);
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Metadata"), "The name 'Metadata' is not a legal name for a resource");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("History"), "The name 'History' is not a legal name for a resource");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Tag"), "The name 'Tag' is not a legal name for a resource");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Tags"), "The name 'Tags' is not a legal name for a resource");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("MailBox"), "The name 'MailBox' is not a legal name for a resource");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Validation"), "The name 'Validation' is not a legal name for a resource");
    rule(errors, IssueType.REQUIRED, rd.getName(), name.equals("Parameters") || translations.hasTranslation(name), "The name '" + name + "' is not found in the file translations.xml");
    rule(errors, IssueType.STRUCTURE, rd.getName(), name.length() > 1 && Character.isUpperCase(name.charAt(0)), "Resource Name must start with an uppercase alpha character");
    rule(errors, IssueType.STRUCTURE, rd.getName(), !Utilities.noString(rd.getFmmLevel()), "Resource must have a maturity level");
    // too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
    rule(errors, IssueType.REQUIRED, rd.getName(), rd.getRoot().getElements().size() > 0, "A resource must have at least one element in it before the build can proceed");
    // too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
    rule(errors, IssueType.REQUIRED, rd.getName(), rd.getWg() != null, "A resource must have a designated owner");
    rule(errors, IssueType.REQUIRED, rd.getName(), !Utilities.noString(rd.getRoot().getW5()), "A resource must have a W5 category");
    rd.getRoot().setMinCardinality(0);
    rd.getRoot().setMaxCardinality(Integer.MAX_VALUE);
    // pattern related rules
    buildW5Mappings(rd.getRoot(), true);
    if ((isWorkflowPattern(rd, "Event") || isWorkflowPattern(rd, "Request")) && hasPatient(rd)) {
        rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("patient"), "An 'event' or 'request' resource must have a search parameter 'patient'");
    }
    if (suppressedwarning(errors, IssueType.REQUIRED, rd.getName(), hasW5Mappings(rd) || rd.getName().equals("Binary") || rd.getName().equals("OperationOutcome") || rd.getName().equals("Parameters"), "A resource must have w5 mappings")) {
        String w5Order = listW5Elements(rd);
        String w5CorrectOrder = listW5Correct(rd);
        if (!w5Order.equals(w5CorrectOrder)) {
            warning(errors, IssueType.REQUIRED, rd.getName(), false, "Resource elements are out of order. The correct order is '" + w5CorrectOrder + "' but the actual order is '" + w5Order + "'");
        // System.out.println("Resource "+parent.getName()+": elements are out of order. The correct order is '"+w5CorrectOrder+"' but the actual order is '"+w5Order+"'");
        }
        if (!Utilities.noString(rd.getProposedOrder())) {
            w5Order = listW5Elements(rd, rd.getProposedOrder());
            if (!w5Order.equals(w5CorrectOrder)) {
                rule(errors, IssueType.REQUIRED, rd.getName(), false, "Proposed Resource elements are out of order. The correct order is '" + w5CorrectOrder + "' but the proposed order is '" + w5Order + "'");
            } else
                System.out.println("Proposed order for " + rd.getName() + ": build order ok");
        }
    }
    if (Utilities.noString(rd.getEnteredInErrorStatus()))
        if (hasStatus(rd, "entered-in-error"))
            rd.setEnteredInErrorStatus(".status = entered-in-error");
        else if (hasStatus(rd, "retired"))
            rd.setEnteredInErrorStatus(".status = retired");
        else if (hasActivFalse(rd))
            rd.setEnteredInErrorStatus(".active = false");
        else
            // too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
            warning(errors, IssueType.REQUIRED, rd.getName(), false, "A resource must have an 'entered in error' status");
    String s = rd.getRoot().getMapping(Definitions.RIM_MAPPING);
    hint(errors, IssueType.REQUIRED, rd.getName(), !Utilities.noString(s), "RIM Mapping is required");
    for (Operation op : rd.getOperations()) {
        warning(errors, IssueType.BUSINESSRULE, rd.getName() + ".$" + op.getName(), hasOpExample(op.getExamples(), false), "Operation must have an example request");
        warning(errors, IssueType.BUSINESSRULE, rd.getName() + ".$" + op.getName(), hasOpExample(op.getExamples(), true), "Operation must have an example response");
    }
    List<String> vsWarns = new ArrayList<String>();
    int vsWarnings = checkElement(errors, rd.getName(), rd.getRoot(), rd, null, s == null || !s.equalsIgnoreCase("n/a"), false, hasSummary(rd.getRoot()), vsWarns, true, rd.getStatus());
    if (!resourceIsTechnical(name)) {
        // these are exempt because identification is tightly managed
        ElementDefn id = rd.getRoot().getElementByName(definitions, "identifier", true, false);
        if (id == null)
            warning(errors, IssueType.STRUCTURE, rd.getName(), false, "All resources should have an identifier");
        else
            rule(errors, IssueType.STRUCTURE, rd.getName(), id.typeCode().equals("Identifier"), "If a resource has an element named identifier, it must have a type 'Identifier'");
    }
    rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getRoot().getElementByName(definitions, "text", true, false) == null, "Element named \"text\" not allowed");
    rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getRoot().getElementByName(definitions, "contained", true, false) == null, "Element named \"contained\" not allowed");
    if (rd.getRoot().getElementByName(definitions, "subject", true, false) != null && rd.getRoot().getElementByName(definitions, "subject", true, false).typeCode().startsWith("Reference"))
        rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("subject"), "A resource that contains a subject reference must have a search parameter 'subject'");
    ElementDefn ped = rd.getRoot().getElementByName(definitions, "patient", true, false);
    if (ped != null && ped.typeCode().startsWith("Reference")) {
        SearchParameterDefn spd = rd.getSearchParams().get("patient");
        if (rule(errors, IssueType.STRUCTURE, rd.getName(), spd != null, "A resource that contains a patient reference must have a search parameter 'patient'"))
            rule(errors, IssueType.STRUCTURE, rd.getName(), (spd.getTargets().size() == 1 && spd.getTargets().contains("Patient")) || (ped.getTypes().get(0).getParams().size() == 1 && ped.getTypes().get(0).getParams().get(0).equals("Patient")), "A Patient search parameter must only refer to patient");
    }
    ElementDefn sed = rd.getRoot().getElementByName(definitions, "subject", true, false);
    if (sed != null && sed.typeCode().startsWith("Reference") && sed.typeCode().contains("Patient"))
        warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("patient"), "A resource that contains a subject that can be a patient reference must have a search parameter 'patient'");
    if (rd.getRoot().getElementByName(definitions, "identifier", true, false) != null) {
        warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("identifier"), "A resource that contains an identifier must have a search parameter 'identifier'");
    }
    if (rd.getRoot().getElementByName(definitions, "status", true, false) != null) {
        // todo: change to a warning post STU3
        hint(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("status"), "A resource that contains a status element must have a search parameter 'status'");
    }
    if (rd.getRoot().getElementByName(definitions, "url", true, false) != null) {
        warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("url"), "A resource that contains a url element must have a search parameter 'url'");
    }
    checkSearchParams(errors, rd);
    for (Operation op : rd.getOperations()) {
        rule(errors, IssueType.STRUCTURE, rd.getName() + "/$" + op.getName(), !parentHasOp(rd.getRoot().typeCode(), op.getName()), "Duplicate Operation Name $" + op.getName() + " on " + rd.getName());
    }
    for (Compartment c : definitions.getCompartments()) {
        if (rule(errors, IssueType.STRUCTURE, rd.getName(), name.equals("Parameters") || c.getResources().containsKey(rd), "Resource not entered in resource map for compartment '" + c.getTitle() + "' (compartments.xml)")) {
            String param = c.getResources().get(rd);
            if (!Utilities.noString(param)) {
                // rule(errors, IssueType.STRUCTURE, parent.getName(), param.equals("{def}") || parent.getSearchParams().containsKey(c.getName()), "Resource "+parent.getName()+" in compartment " +c.getName()+" must have a search parameter named "+c.getName().toLowerCase()+")");
                for (String p : param.split("\\|")) {
                    String pn = p.trim();
                    if (pn.contains("."))
                        pn = pn.substring(0, pn.indexOf("."));
                    rule(errors, IssueType.STRUCTURE, rd.getName(), Utilities.noString(pn) || pn.equals("{def}") || rd.getSearchParams().containsKey(pn), "Resource " + rd.getName() + " in compartment " + c.getName() + ": parameter " + param + " was not found (" + pn + ")");
                }
            }
        }
    }
    // Remove suppressed messages
    List<ValidationMessage> suppressed = new ArrayList<ValidationMessage>();
    for (ValidationMessage em : errors) {
        if (isSuppressedMessage(em.getDisplay())) {
            suppressed.add(em);
        }
    }
    errors.removeAll(suppressed);
    // last check: if maturity level is
    int warnings = 0;
    int hints = 0;
    for (ValidationMessage em : errors) {
        if (em.getLevel() == IssueSeverity.WARNING)
            warnings++;
        else if (em.getLevel() == IssueSeverity.INFORMATION)
            hints++;
    }
    boolean ok = warnings == 0 || "0".equals(rd.getFmmLevel());
    if (rule(errors, IssueType.STRUCTURE, rd.getName(), ok, "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >1 (" + rd.getFmmLevel() + ") if it has warnings"))
        rule(errors, IssueType.STRUCTURE, rd.getName(), vsWarnings == 0 || "0".equals(rd.getFmmLevel()), "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >1 (" + rd.getFmmLevel() + ") if it has linked value set warnings (" + vsWarns.toString() + ")");
    ok = hints == 0 || Integer.parseInt(rd.getFmmLevel()) < 3;
    rule(errors, IssueType.STRUCTURE, rd.getName(), ok, "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >2 (" + rd.getFmmLevel() + ") if it has informational hints");
// if (isInterface(rd.getRoot().typeCode())) {
// checkInterface(errors, rd, definitions.getBaseResources().get(rd.getRoot().typeCode()));
// }
}
Also used : ValidationMessage(org.hl7.fhir.utilities.validation.ValidationMessage)

Example 65 with ElementDefn

use of org.hl7.fhir.definitions.model.ElementDefn in project kindling by HL7.

the class ProfileGenerator method makeExtensionSlice.

private ElementDefinition makeExtensionSlice(String extensionName, StructureDefinition p, StructureDefinitionSnapshotComponent c, ElementDefn e, String path) throws URISyntaxException, Exception {
    ElementDefinition ex = createBaseDefinition(p, path, definitions.getBaseResources().get("DomainResource").getRoot().getElementByName(definitions, extensionName, false, false, null));
    c.getElement().add(ex);
    if (!ex.hasBase())
        ex.setBase(new ElementDefinitionBaseComponent());
    ex.getBase().setPath("Element.extension");
    ex.getBase().setMin(0);
    ex.getBase().setMax("*");
    ElementDefinitionConstraintComponent inv = ex.addConstraint();
    inv.setKey("ext-1");
    inv.setSeverity(ConstraintSeverity.ERROR);
    inv.setHuman("Must have either extensions or value[x], not both");
    inv.setExpression("extension.exists() != value.exists()");
    inv.setXpath("exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])");
    inv.setSource("http://hl7.org/fhir/StructureDefinition/Extension");
    return ex;
}
Also used : ElementDefinition(org.hl7.fhir.r5.model.ElementDefinition) ElementDefinitionBaseComponent(org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBaseComponent) ElementDefinitionConstraintComponent(org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent)

Aggregations

ElementDefn (org.hl7.fhir.definitions.model.ElementDefn)100 TypeRef (org.hl7.fhir.definitions.model.TypeRef)35 ArrayList (java.util.ArrayList)28 FHIRException (org.hl7.fhir.exceptions.FHIRException)28 CommaSeparatedStringBuilder (org.hl7.fhir.utilities.CommaSeparatedStringBuilder)21 ResourceDefn (org.hl7.fhir.definitions.model.ResourceDefn)19 Invariant (org.hl7.fhir.definitions.model.Invariant)16 IOException (java.io.IOException)14 BindingSpecification (org.hl7.fhir.definitions.model.BindingSpecification)11 FileNotFoundException (java.io.FileNotFoundException)10 URISyntaxException (java.net.URISyntaxException)10 StructureDefinition (org.hl7.fhir.r5.model.StructureDefinition)10 ProfiledType (org.hl7.fhir.definitions.model.ProfiledType)9 TransformerException (javax.xml.transform.TransformerException)8 ElementDefinition (org.hl7.fhir.r5.model.ElementDefinition)8 TransformerConfigurationException (javax.xml.transform.TransformerConfigurationException)7 NotImplementedException (org.apache.commons.lang3.NotImplementedException)7 UcumException (org.fhir.ucum.UcumException)7 TypeParser (org.hl7.fhir.definitions.parsers.TypeParser)7 DefinitionException (org.hl7.fhir.exceptions.DefinitionException)7