Search in sources :

Example 16 with Slice

use of org.hl7.elm.r1.Slice in project org.hl7.fhir.core by hapifhir.

the class ProfileUtilities method processPaths.

/**
 * @param trimDifferential
 * @throws DefinitionException, FHIRException
 * @throws Exception
 */
private void processPaths(StructureDefinitionSnapshotComponent result, StructureDefinitionSnapshotComponent base, StructureDefinitionDifferentialComponent differential, int baseCursor, int diffCursor, int baseLimit, int diffLimit, String url, String profileName, String contextPath, boolean trimDifferential, String contextName, String resultPathBase, boolean slicingDone) throws DefinitionException, FHIRException {
    // just repeat processing entries until we run out of our allowed scope (1st entry, the allowed scope is all the entries)
    while (baseCursor <= baseLimit) {
        // get the current focus of the base, and decide what to do
        ElementDefinition currentBase = base.getElement().get(baseCursor);
        String cpath = fixedPath(contextPath, currentBase.getPath());
        // get a list of matching elements in scope
        List<ElementDefinition> diffMatches = getDiffMatches(differential, cpath, diffCursor, diffLimit, profileName);
        // in the simple case, source is not sliced.
        if (!currentBase.hasSlicing()) {
            if (diffMatches.isEmpty()) {
                // the differential doesn't say anything about this item
                // so we just copy it in
                ElementDefinition outcome = updateURLs(url, currentBase.copy());
                outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                updateFromBase(outcome, currentBase);
                markDerived(outcome);
                if (resultPathBase == null)
                    resultPathBase = outcome.getPath();
                else if (!outcome.getPath().startsWith(resultPathBase))
                    throw new DefinitionException("Adding wrong path");
                result.getElement().add(outcome);
                baseCursor++;
            } else if (diffMatches.size() == 1 && (slicingDone || (!diffMatches.get(0).hasSlicing() && !(isExtension(diffMatches.get(0)) && !diffMatches.get(0).hasName())))) {
                // one matching element in the differential
                ElementDefinition template = null;
                if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !diffMatches.get(0).getType().get(0).getCode().equals("Reference")) {
                    String p = diffMatches.get(0).getType().get(0).getProfile().get(0).asStringValue();
                    StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
                    if (sd != null) {
                        if (!sd.hasSnapshot()) {
                            StructureDefinition sdb = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
                            if (sdb == null)
                                throw new DefinitionException("no base for " + sd.getBaseDefinition());
                            generateSnapshot(sdb, sd, sd.getUrl(), sd.getName());
                        }
                        template = sd.getSnapshot().getElement().get(0).copy().setPath(currentBase.getPath());
                        // temporary work around
                        if (!diffMatches.get(0).getType().get(0).getCode().equals("Extension")) {
                            template.setMin(currentBase.getMin());
                            template.setMax(currentBase.getMax());
                        }
                    }
                }
                if (template == null)
                    template = currentBase.copy();
                else
                    // some of what's in currentBase overrides template
                    template = overWriteWithCurrent(template, currentBase);
                ElementDefinition outcome = updateURLs(url, template);
                outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                updateFromBase(outcome, currentBase);
                if (diffMatches.get(0).hasName())
                    outcome.setName(diffMatches.get(0).getName());
                outcome.setSlicing(null);
                updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url);
                if (// if the base profile allows multiple types, but the profile only allows one, rename it
                outcome.getPath().endsWith("[x]") && outcome.getType().size() == 1 && !outcome.getType().get(0).getCode().equals("*"))
                    outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length() - 3) + Utilities.capitalize(outcome.getType().get(0).getCode()));
                if (resultPathBase == null)
                    resultPathBase = outcome.getPath();
                else if (!outcome.getPath().startsWith(resultPathBase))
                    throw new DefinitionException("Adding wrong path");
                result.getElement().add(outcome);
                baseCursor++;
                diffCursor = differential.getElement().indexOf(diffMatches.get(0)) + 1;
                if (differential.getElement().size() > diffCursor && outcome.getPath().contains(".") && isDataType(outcome.getType())) {
                    // don't want to do this for the root, since that's base, and we're already processing it
                    if (pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) {
                        if (outcome.getType().size() > 1)
                            throw new DefinitionException(diffMatches.get(0).getPath() + " has children (" + differential.getElement().get(diffCursor).getPath() + ") and multiple types (" + typeCode(outcome.getType()) + ") in profile " + profileName);
                        StructureDefinition dt = getProfileForDataType(outcome.getType().get(0));
                        if (dt == null)
                            throw new DefinitionException(diffMatches.get(0).getPath() + " has children (" + differential.getElement().get(diffCursor).getPath() + ") for type " + typeCode(outcome.getType()) + " in profile " + profileName + ", but can't find type");
                        contextName = dt.getUrl();
                        int start = diffCursor;
                        while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) diffCursor++;
                        processPaths(result, dt.getSnapshot(), differential, 1, /* starting again on the data type, but skip the root */
                        start - 1, dt.getSnapshot().getElement().size() - 1, diffCursor - 1, url, profileName + pathTail(diffMatches, 0), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false);
                    }
                }
            } else {
                // ok, the differential slices the item. Let's check our pre-conditions to ensure that this is correct
                if (!unbounded(currentBase) && !isSlicedToOneOnly(diffMatches.get(0)))
                    // (but you might do that in order to split up constraints by type)
                    throw new DefinitionException("Attempt to a slice an element that does not repeat: " + currentBase.getPath() + "/" + currentBase.getName() + " from " + contextName);
                if (// well, the diff has set up a slice, but hasn't defined it. this is an error
                !diffMatches.get(0).hasSlicing() && !isExtension(currentBase))
                    throw new DefinitionException("differential does not have a slice: " + currentBase.getPath());
                // well, if it passed those preconditions then we slice the dest.
                // we're just going to accept the differential slicing at face value
                ElementDefinition outcome = updateURLs(url, currentBase.copy());
                outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                updateFromBase(outcome, currentBase);
                if (!diffMatches.get(0).hasSlicing())
                    outcome.setSlicing(makeExtensionSlicing());
                else
                    outcome.setSlicing(diffMatches.get(0).getSlicing().copy());
                if (!outcome.getPath().startsWith(resultPathBase))
                    throw new DefinitionException("Adding wrong path");
                result.getElement().add(outcome);
                // differential - if the first one in the list has a name, we'll process it. Else we'll treat it as the base definition of the slice.
                int start = 0;
                if (!diffMatches.get(0).hasName()) {
                    updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url);
                    if (!outcome.hasType()) {
                        throw new DefinitionException("not done yet");
                    }
                    start = 1;
                } else
                    checkExtensionDoco(outcome);
                // now, for each entry in the diff matches, we're going to process the base item
                // our processing scope for base is all the children of the current path
                int nbl = findEndOfElement(base, baseCursor);
                int ndc = diffCursor;
                int ndl = diffCursor;
                for (int i = start; i < diffMatches.size(); i++) {
                    // our processing scope for the differential is the item in the list, and all the items before the next one in the list
                    ndc = differential.getElement().indexOf(diffMatches.get(i));
                    ndl = findEndOfElement(differential, ndc);
                    // now we process the base scope repeatedly for each instance of the item in the differential list
                    processPaths(result, base, differential, baseCursor, ndc, nbl, ndl, url, profileName + pathTail(diffMatches, i), contextPath, trimDifferential, contextName, resultPathBase, true);
                }
                // ok, done with that - next in the base list
                baseCursor = nbl + 1;
                diffCursor = ndl + 1;
            }
        } else {
            // the item is already sliced in the base profile.
            // here's the rules
            // 1. irrespective of whether the slicing is ordered or not, the definition order must be maintained
            // 2. slice element names have to match.
            // 3. new slices must be introduced at the end
            // corallory: you can't re-slice existing slices. is that ok?
            // we're going to need this:
            String path = currentBase.getPath();
            ElementDefinition original = currentBase;
            if (diffMatches.isEmpty()) {
                // copy across the currentbase, and all of it's children and siblings
                while (baseCursor < base.getElement().size() && base.getElement().get(baseCursor).getPath().startsWith(path)) {
                    ElementDefinition outcome = updateURLs(url, base.getElement().get(baseCursor).copy());
                    if (!outcome.getPath().startsWith(resultPathBase))
                        throw new DefinitionException("Adding wrong path: " + outcome.getPath() + " vs " + resultPathBase);
                    // so we just copy it in
                    result.getElement().add(outcome);
                    baseCursor++;
                }
            } else {
                // first - check that the slicing is ok
                boolean closed = currentBase.getSlicing().getRules() == SlicingRules.CLOSED;
                int diffpos = 0;
                boolean isExtension = cpath.endsWith(".extension") || cpath.endsWith(".modifierExtension");
                if (diffMatches.get(0).hasSlicing()) {
                    // it might be null if the differential doesn't want to say anything about slicing
                    if (!isExtension)
                        // if there's a slice on the first, we'll ignore any content it has
                        diffpos++;
                    ElementDefinitionSlicingComponent dSlice = diffMatches.get(0).getSlicing();
                    ElementDefinitionSlicingComponent bSlice = currentBase.getSlicing();
                    if (!orderMatches(dSlice.getOrderedElement(), bSlice.getOrderedElement()))
                        throw new DefinitionException("Slicing rules on differential (" + summariseSlicing(dSlice) + ") do not match those on base (" + summariseSlicing(bSlice) + ") - order @ " + path + " (" + contextName + ")");
                    if (!discriiminatorMatches(dSlice.getDiscriminator(), bSlice.getDiscriminator()))
                        throw new DefinitionException("Slicing rules on differential (" + summariseSlicing(dSlice) + ") do not match those on base (" + summariseSlicing(bSlice) + ") - disciminator @ " + path + " (" + contextName + ")");
                    if (!ruleMatches(dSlice.getRules(), bSlice.getRules()))
                        throw new DefinitionException("Slicing rules on differential (" + summariseSlicing(dSlice) + ") do not match those on base (" + summariseSlicing(bSlice) + ") - rule @ " + path + " (" + contextName + ")");
                }
                ElementDefinition outcome = updateURLs(url, currentBase.copy());
                outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                updateFromBase(outcome, currentBase);
                if (diffMatches.get(0).hasSlicing() && !isExtension) {
                    updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing());
                    // if there's no slice, we don't want to update the unsliced description
                    updateFromDefinition(outcome, diffMatches.get(0), profileName, closed, url);
                }
                if (diffMatches.get(0).hasSlicing() && !diffMatches.get(0).hasName())
                    diffpos++;
                result.getElement().add(outcome);
                // now, we have two lists, base and diff. we're going to work through base, looking for matches in diff.
                List<ElementDefinition> baseMatches = getSiblings(base.getElement(), currentBase);
                for (ElementDefinition baseItem : baseMatches) {
                    baseCursor = base.getElement().indexOf(baseItem);
                    outcome = updateURLs(url, baseItem.copy());
                    updateFromBase(outcome, currentBase);
                    outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                    outcome.setSlicing(null);
                    if (!outcome.getPath().startsWith(resultPathBase))
                        throw new DefinitionException("Adding wrong path");
                    if (diffpos < diffMatches.size() && diffMatches.get(diffpos).getName().equals(outcome.getName())) {
                        // if there's a diff, we update the outcome with diff
                        // no? updateFromDefinition(outcome, diffMatches.get(diffpos), profileName, closed, url);
                        // then process any children
                        int nbl = findEndOfElement(base, baseCursor);
                        int ndc = differential.getElement().indexOf(diffMatches.get(diffpos));
                        int ndl = findEndOfElement(differential, ndc);
                        // now we process the base scope repeatedly for each instance of the item in the differential list
                        processPaths(result, base, differential, baseCursor, ndc, nbl, ndl, url, profileName + pathTail(diffMatches, diffpos), contextPath, closed, contextName, resultPathBase, true);
                        // ok, done with that - now set the cursors for if this is the end
                        baseCursor = nbl + 1;
                        diffCursor = ndl + 1;
                        diffpos++;
                    } else {
                        result.getElement().add(outcome);
                        baseCursor++;
                        // just copy any children on the base
                        while (baseCursor < base.getElement().size() && base.getElement().get(baseCursor).getPath().startsWith(path) && !base.getElement().get(baseCursor).getPath().equals(path)) {
                            outcome = updateURLs(url, currentBase.copy());
                            outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                            if (!outcome.getPath().startsWith(resultPathBase))
                                throw new DefinitionException("Adding wrong path");
                            result.getElement().add(outcome);
                            baseCursor++;
                        }
                    }
                }
                // finally, we process any remaining entries in diff, which are new (and which are only allowed if the base wasn't closed
                if (closed && diffpos < diffMatches.size())
                    throw new DefinitionException("The base snapshot marks a slicing as closed, but the differential tries to extend it in " + profileName + " at " + path + " (" + cpath + ")");
                while (diffpos < diffMatches.size()) {
                    ElementDefinition diffItem = diffMatches.get(diffpos);
                    for (ElementDefinition baseItem : baseMatches) if (baseItem.getName().equals(diffItem.getName()))
                        throw new DefinitionException("Named items are out of order in the slice");
                    outcome = updateURLs(url, original.copy());
                    outcome.setPath(fixedPath(contextPath, outcome.getPath()));
                    updateFromBase(outcome, currentBase);
                    outcome.setSlicing(null);
                    if (!outcome.getPath().startsWith(resultPathBase))
                        throw new DefinitionException("Adding wrong path");
                    result.getElement().add(outcome);
                    updateFromDefinition(outcome, diffItem, profileName, trimDifferential, url);
                    diffpos++;
                }
            }
        }
    }
}
Also used : StructureDefinition(org.hl7.fhir.dstu2016may.model.StructureDefinition) ElementDefinitionSlicingComponent(org.hl7.fhir.dstu2016may.model.ElementDefinition.ElementDefinitionSlicingComponent) ElementDefinition(org.hl7.fhir.dstu2016may.model.ElementDefinition) DefinitionException(org.hl7.fhir.exceptions.DefinitionException)

Example 17 with Slice

use of org.hl7.elm.r1.Slice in project org.hl7.fhir.core by hapifhir.

the class ProfileUtilitiesTests method testSlicingExtension.

/**
 * we're going to slice Patient.extension and refer to extension by profile
 *
 * implicit: whether to rely on implicit extension slicing
 */
private void testSlicingExtension(boolean implicit) throws EOperationOutcome, Exception {
    StructureDefinition focus = new StructureDefinition();
    StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
    focus.setUrl(Utilities.makeUuidUrn());
    focus.setBaseDefinition(base.getUrl());
    focus.setType(base.getType());
    focus.setDerivation(TypeDerivationRule.CONSTRAINT);
    // set the slice up
    ElementDefinition id;
    if (!implicit) {
        id = focus.getDifferential().addElement();
        id.setPath("Patient.extension");
        id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("url").setType(DiscriminatorType.VALUE);
        id.setMax("3");
    }
    // first slice:
    id = focus.getDifferential().addElement();
    id.setPath("Patient.extension");
    id.setSliceName("name1");
    id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-birthTime");
    id.setMin(1);
    // second slice:
    id = focus.getDifferential().addElement();
    id.setPath("Patient.extension");
    id.setSliceName("name2");
    id.addType().setCode("Extension").setProfile("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName");
    List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
    ProfileUtilities pu = new ProfileUtilities(context, messages, null);
    pu.generateSnapshot(base, focus, focus.getUrl(), "Simple Test");
    // 2 different: extension slices
    boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 2;
    for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
        if (ok) {
            ElementDefinition b = base.getSnapshot().getElement().get(i);
            ElementDefinition f = focus.getSnapshot().getElement().get(i <= 7 ? i : i + 2);
            if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
                ok = false;
            else {
                f.setBase(null);
                if (f.getPath().equals("Patient.extension")) {
                    ok = f.hasSlicing() && (implicit || f.getMax().equals("3"));
                    if (ok) {
                        f.setSlicing(null);
                        f.setMaxElement(b.getMaxElement());
                    }
                }
                if (// no compare that because the definitions get overwritten
                !f.getPath().equals("Patient.extension"))
                    ok = Base.compareDeep(b, f, true);
            }
        }
    }
    // now, check that the slices we skipped are correct:
    if (ok) {
        ElementDefinition d1 = focus.getSnapshot().getElement().get(8);
        ElementDefinition d2 = focus.getSnapshot().getElement().get(9);
        ok = d1.hasType() && d1.getType().get(0).hasProfile() && d2.hasType() && d2.getType().get(0).hasProfile() && !Base.compareDeep(d1.getType(), d2.getType(), true) && d1.getMin() == 1 && d2.getMin() == 0 && d1.getMax().equals("1") && d2.getMax().equals("1");
        if (ok) {
            d1.getType().clear();
            d2.getType().clear();
            d1.setSliceName("x");
            d2.setSliceName("x");
            d1.setMin(0);
        }
        ok = Base.compareDeep(d1, d2, true);
    // for throughness, we could check against extension too, but this is not done now.
    }
    if (!ok) {
        compareXml(base, focus);
        throw new FHIRException("Snap shot generation slicing extensions simple (" + (implicit ? "implicit" : "not implicit") + ") failed");
    } else
        System.out.println("Snap shot generation slicing extensions simple (" + (implicit ? "implicit" : "not implicit") + ") passed");
}
Also used : StructureDefinition(org.hl7.fhir.dstu3.model.StructureDefinition) ValidationMessage(org.hl7.fhir.utilities.validation.ValidationMessage) ProfileUtilities(org.hl7.fhir.dstu3.conformance.ProfileUtilities) ArrayList(java.util.ArrayList) ElementDefinition(org.hl7.fhir.dstu3.model.ElementDefinition) FHIRException(org.hl7.fhir.exceptions.FHIRException)

Example 18 with Slice

use of org.hl7.elm.r1.Slice in project org.hl7.fhir.core by hapifhir.

the class ProfileUtilitiesTests method testSlicingSimple.

/**
 * we're going to slice Patient.identifier
 */
private void testSlicingSimple() throws EOperationOutcome, Exception {
    StructureDefinition focus = new StructureDefinition();
    StructureDefinition base = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/Patient").copy();
    focus.setUrl(Utilities.makeUuidUrn());
    focus.setBaseDefinition(base.getUrl());
    focus.setType(base.getType());
    focus.setDerivation(TypeDerivationRule.CONSTRAINT);
    // set the slice up
    ElementDefinition id = focus.getDifferential().addElement();
    id.setPath("Patient.identifier");
    id.getSlicing().setOrdered(false).setRules(SlicingRules.OPEN).addDiscriminator().setPath("use").setType(DiscriminatorType.VALUE);
    // first slice:
    id = focus.getDifferential().addElement();
    id.setPath("Patient.identifier");
    id.setSliceName("name1");
    id = focus.getDifferential().addElement();
    id.setPath("Patient.identifier.use");
    id.setFixed(new CodeType("usual"));
    // second slice:
    id = focus.getDifferential().addElement();
    id.setPath("Patient.identifier");
    id.setSliceName("name2");
    id = focus.getDifferential().addElement();
    id.setPath("Patient.identifier.use");
    id.setFixed(new CodeType("official"));
    List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
    new ProfileUtilities(context, messages, null).generateSnapshot(base, focus, focus.getUrl(), "Simple Test");
    // 18 different: identifier + 8 inner children * 2
    boolean ok = base.getSnapshot().getElement().size() == focus.getSnapshot().getElement().size() - 18;
    for (int i = 0; i < base.getSnapshot().getElement().size(); i++) {
        if (ok) {
            ElementDefinition b = base.getSnapshot().getElement().get(i);
            ElementDefinition f = focus.getSnapshot().getElement().get(i <= 9 ? i : i + 18);
            if (!f.hasBase() || !b.getPath().equals(f.getBase().getPath()))
                ok = false;
            else {
                f.setBase(null);
                if (f.getPath().equals("Patient.identifier")) {
                    ok = f.hasSlicing();
                    if (ok)
                        f.setSlicing(null);
                }
                ok = Base.compareDeep(b, f, true);
            }
        }
    }
    // now, check that the slices we skipped are correct:
    for (int i = 10; i <= 18; i++) {
        if (ok) {
            ElementDefinition d1 = focus.getSnapshot().getElement().get(i);
            ElementDefinition d2 = focus.getSnapshot().getElement().get(i + 9);
            if (d1.getPath().equals("Patient.identifier.use")) {
                ok = d1.hasFixed() && d2.hasFixed() && !Base.compareDeep(d1.getFixed(), d2.getFixed(), true);
                if (ok) {
                    d1.setFixed(null);
                    d2.setFixed(null);
                }
            }
            if (d1.getPath().equals("Patient.identifier")) {
                ok = d1.hasSliceName() && d2.hasSliceName() && !Base.compareDeep(d1.getSliceNameElement(), d2.getSliceNameElement(), true);
                if (ok) {
                    d1.setSliceName(null);
                    d2.setSliceName(null);
                }
            }
            ok = Base.compareDeep(d1, d2, true);
        }
    }
    if (!ok) {
        compareXml(base, focus);
        throw new FHIRException("Snap shot generation slicing failed");
    } else
        System.out.println("Snap shot generation slicing passed");
}
Also used : StructureDefinition(org.hl7.fhir.dstu3.model.StructureDefinition) ValidationMessage(org.hl7.fhir.utilities.validation.ValidationMessage) ProfileUtilities(org.hl7.fhir.dstu3.conformance.ProfileUtilities) ArrayList(java.util.ArrayList) CodeType(org.hl7.fhir.dstu3.model.CodeType) ElementDefinition(org.hl7.fhir.dstu3.model.ElementDefinition) FHIRException(org.hl7.fhir.exceptions.FHIRException)

Example 19 with Slice

use of org.hl7.elm.r1.Slice in project org.hl7.fhir.core by hapifhir.

the class FHIRPathEngine method evaluateDefinition.

/**
 * given an element definition in a profile, what element contains the differentiating fixed
 * for the element, given the differentiating expresssion. The expression is only allowed to
 * use a subset of FHIRPath
 *
 * @param profile
 * @param element
 * @return
 * @throws PathEngineException
 * @throws DefinitionException
 */
public ElementDefinition evaluateDefinition(ExpressionNode expr, StructureDefinition profile, ElementDefinition element) throws DefinitionException {
    StructureDefinition sd = profile;
    ElementDefinition focus = null;
    if (expr.getKind() == Kind.Name) {
        if (element.hasSlicing()) {
            ElementDefinition slice = pickMandatorySlice(sd, element);
            if (slice == null)
                throw new DefinitionException("Error in discriminator at " + element.getId() + ": found a sliced element while resolving the fixed value for one of the slices");
            element = slice;
        }
        if (expr.getName().equals("$this")) {
            focus = element;
        } else {
            List<ElementDefinition> childDefinitions;
            childDefinitions = ProfileUtilities.getChildMap(sd, element);
            // if that's empty, get the children of the type
            if (childDefinitions.isEmpty()) {
                sd = fetchStructureByType(element);
                if (sd == null)
                    throw new DefinitionException("Problem with use of resolve() - profile '" + element.getType().get(0).getProfile() + "' on " + element.getId() + " could not be resolved");
                childDefinitions = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep());
            }
            for (ElementDefinition t : childDefinitions) {
                if (tailMatches(t, expr.getName())) {
                    focus = t;
                    break;
                }
            }
        }
    } else if (expr.getKind() == Kind.Function) {
        if ("resolve".equals(expr.getName())) {
            if (!element.hasType())
                throw new DefinitionException("illegal use of resolve() in discriminator - no type on element " + element.getId());
            if (element.getType().size() > 1)
                throw new DefinitionException("illegal use of resolve() in discriminator - Multiple possible types on " + element.getId());
            if (!element.getType().get(0).hasTarget())
                throw new DefinitionException("illegal use of resolve() in discriminator - type on " + element.getId() + " is not Reference (" + element.getType().get(0).getCode() + ")");
            if (element.getType().get(0).getTargetProfile().size() > 1)
                throw new DefinitionException("illegal use of resolve() in discriminator - Multiple possible target type profiles on " + element.getId());
            sd = worker.fetchResource(StructureDefinition.class, element.getType().get(0).getTargetProfile().get(0).getValue());
            if (sd == null)
                throw new DefinitionException("Problem with use of resolve() - profile '" + element.getType().get(0).getTargetProfile() + "' on " + element.getId() + " could not be resolved");
            focus = sd.getSnapshot().getElementFirstRep();
        } else if ("extension".equals(expr.getName())) {
            String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue();
            List<ElementDefinition> childDefinitions = ProfileUtilities.getChildMap(sd, element);
            for (ElementDefinition t : childDefinitions) {
                if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
                    StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty()) ? null : worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue());
                    while (exsd != null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition());
                    if (exsd.getUrl().equals(targetUrl)) {
                        if (ProfileUtilities.getChildMap(sd, t).isEmpty())
                            sd = exsd;
                        focus = t;
                        break;
                    }
                }
            }
        } else
            throw new DefinitionException("illegal function name " + expr.getName() + "() in discriminator");
    } else if (expr.getKind() == Kind.Group) {
        throw new DefinitionException("illegal expression syntax in discriminator (group)");
    } else if (expr.getKind() == Kind.Constant) {
        throw new DefinitionException("illegal expression syntax in discriminator (const)");
    }
    if (focus == null)
        throw new DefinitionException("Unable to resolve discriminator in definitions: " + expr.toString());
    else if (expr.getInner() == null)
        return focus;
    else {
        return evaluateDefinition(expr.getInner(), sd, focus);
    }
}
Also used : DefinitionException(org.hl7.fhir.exceptions.DefinitionException)

Example 20 with Slice

use of org.hl7.elm.r1.Slice in project org.hl7.fhir.core by hapifhir.

the class ProfileUtilities method generateDescription.

private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows, RenderingContext rc) throws IOException, FHIRException {
    Cell c = gen.new Cell();
    row.getCells().add(c);
    if (used) {
        if (logicalModel && ToolingExtensions.hasExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) {
            if (root) {
                c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace") + ": ", null).addStyle("font-weight:bold"));
                c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null));
            } else if (!root && ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace") && !ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace").equals(ToolingExtensions.readStringExtension(profile, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))) {
                c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace") + ": ", null).addStyle("font-weight:bold"));
                c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"), null));
            }
        }
        if (root) {
            if (profile != null && profile.getAbstract()) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.addPiece(gen.new Piece(null, "This is an abstract profile", null));
            }
        }
        if (definition.getPath().endsWith("url") && definition.hasFixed()) {
            c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "\"" + buildJson(definition.getFixed()) + "\"", null).addStyle("color: darkgreen")));
        } else {
            if (definition != null && definition.hasShort()) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.addPiece(checkForNoChange(definition.getShortElement(), gen.new Piece(null, gt(definition.getShortElement()), null)));
            } else if (fallback != null && fallback.hasShort()) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.addPiece(gen.new Piece(null, gt(fallback.getShortElement()), null).addStyle("opacity: 0.5"));
            }
            if (url != null) {
                if (!c.getPieces().isEmpty())
                    c.addPiece(gen.new Piece("br"));
                String fullUrl = url.startsWith("#") ? baseURL + url : url;
                StructureDefinition ed = context.fetchResource(StructureDefinition.class, url);
                String ref = null;
                String ref2 = null;
                String fixedUrl = null;
                if (ed != null) {
                    String p = ed.getUserString("path");
                    if (p != null) {
                        ref = p.startsWith("http:") || igmode ? p : Utilities.pathURL(corePath, p);
                    }
                    fixedUrl = getFixedUrl(ed);
                    if (fixedUrl != null) {
                        // if its null, we guess that it's not a profiled extension?
                        if (fixedUrl.equals(url))
                            fixedUrl = null;
                        else {
                            StructureDefinition ed2 = context.fetchResource(StructureDefinition.class, fixedUrl);
                            if (ed2 != null) {
                                String p2 = ed2.getUserString("path");
                                if (p2 != null) {
                                    ref2 = p2.startsWith("http:") || igmode ? p2 : Utilities.pathURL(corePath, p2);
                                }
                            }
                        }
                    }
                }
                if (fixedUrl == null) {
                    c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL") + ": ", null).addStyle("font-weight:bold"));
                    c.getPieces().add(gen.new Piece(ref, fullUrl, null));
                } else {
                    // reference to a profile take on the extension show the base URL
                    c.getPieces().add(gen.new Piece(null, translate("sd.table", "URL") + ": ", null).addStyle("font-weight:bold"));
                    c.getPieces().add(gen.new Piece(ref2, fixedUrl, null));
                    c.getPieces().add(gen.new Piece(null, translate("sd.table", " profiled by ") + " ", null).addStyle("font-weight:bold"));
                    c.getPieces().add(gen.new Piece(ref, fullUrl, null));
                }
            }
            if (definition.hasSlicing()) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice") + ": ", null).addStyle("font-weight:bold"));
                c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null));
            }
            if (!definition.getPath().contains(".") && ToolingExtensions.hasExtension(profile, ToolingExtensions.EXT_BINDING_STYLE)) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.getPieces().add(gen.new Piece(null, translate("sd.table", "Binding") + ": ", null).addStyle("font-weight:bold"));
                c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null));
                c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null));
                c.getPieces().add(gen.new Piece(null, " binding style", null));
            }
            if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) {
                    c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML") + ": ", null).addStyle("font-weight:bold"));
                    c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null));
                    c.getPieces().add(gen.new Piece(null, " (", null));
                    c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null));
                    c.getPieces().add(gen.new Piece(null, ")", null));
                } else {
                    c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Element Name") + ": ", null).addStyle("font-weight:bold"));
                    c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null));
                }
            } else if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) {
                if (!c.getPieces().isEmpty()) {
                    c.addPiece(gen.new Piece("br"));
                }
                c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace") + ": ", null).addStyle("font-weight:bold"));
                c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null));
            }
            if (definition != null) {
                ElementDefinitionBindingComponent binding = null;
                if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty())
                    binding = makeUnifiedBinding(valueDefn.getBinding(), valueDefn);
                else if (definition.hasBinding())
                    binding = makeUnifiedBinding(definition.getBinding(), definition);
                if (binding != null && !binding.isEmpty()) {
                    if (!c.getPieces().isEmpty())
                        c.addPiece(gen.new Piece("br"));
                    BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding, definition.getPath());
                    c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding") + ": ", null).addStyle("font-weight:bold")));
                    c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath + br.url, br.display, null)));
                    if (binding.hasStrength()) {
                        c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, " (", null)));
                        c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath + "terminologies.html#" + binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition())));
                        c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null)));
                    }
                    if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) {
                        c.getPieces().add(gen.new Piece(null, ": ", null));
                        c.addMarkdownNoPara(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement()).asStringValue(), checkForNoChange(PublicationHacker.fixBindingDescriptions(context, binding.getDescriptionElement())));
                    }
                    AdditionalBindingsRenderer abr = new AdditionalBindingsRenderer(pkp, corePath, profile, definition.getPath(), rc, null);
                    if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) {
                        abr.seeMaxBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MAX_VALUESET));
                    }
                    if (binding.hasExtension(ToolingExtensions.EXT_MIN_VALUESET)) {
                        abr.seeMinBinding(ToolingExtensions.getExtension(binding, ToolingExtensions.EXT_MIN_VALUESET));
                    }
                    if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
                        abr.seeAdditionalBindings(binding.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL));
                    }
                    abr.render(gen, c);
                }
                for (ElementDefinitionConstraintComponent inv : definition.getConstraint()) {
                    if (!inv.hasSource() || profile == null || inv.getSource().equals(profile.getUrl()) || allInvariants) {
                        if (!c.getPieces().isEmpty())
                            c.addPiece(gen.new Piece("br"));
                        c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, inv.getKey() + ": ", null).addStyle("font-weight:bold")));
                        c.getPieces().add(checkForNoChange(inv, gen.new Piece(null, gt(inv.getHumanElement()), null)));
                    }
                }
                if ((definition.hasBase() && "*".equals(definition.getBase().getMax())) || (definition.hasMax() && "*".equals(definition.getMax()))) {
                    if (c.getPieces().size() > 0)
                        c.addPiece(gen.new Piece("br"));
                    if (definition.hasOrderMeaning()) {
                        c.getPieces().add(gen.new Piece(null, "This repeating element order: " + definition.getOrderMeaning(), null));
                    } else {
                    // don't show this, this it's important: c.getPieces().add(gen.new Piece(null, "This repeating element has no defined order", null));
                    }
                }
                if (definition.hasFixed()) {
                    if (!c.getPieces().isEmpty()) {
                        c.addPiece(gen.new Piece("br"));
                    }
                    c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value") + ": ", null).addStyle("font-weight:bold")));
                    if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) {
                        String s = buildJson(definition.getFixed());
                        String link = null;
                        if (Utilities.isAbsoluteUrl(s))
                            link = pkp.getLinkForUrl(corePath, s);
                        c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
                    } else {
                        c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen")));
                        genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false);
                    }
                    if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) {
                        Piece p = describeCoded(gen, definition.getFixed());
                        if (p != null)
                            c.getPieces().add(p);
                    }
                } else if (definition.hasPattern()) {
                    if (!c.getPieces().isEmpty()) {
                        c.addPiece(gen.new Piece("br"));
                    }
                    c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern") + ": ", null).addStyle("font-weight:bold")));
                    if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive())
                        c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
                    else {
                        c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen")));
                        genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, mustSupportOnly);
                    }
                } else if (definition.hasExample()) {
                    for (ElementDefinitionExampleComponent ex : definition.getExample()) {
                        if (!c.getPieces().isEmpty()) {
                            c.addPiece(gen.new Piece("br"));
                        }
                        c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, translate("sd.table", "Example") + ("".equals("General") ? "" : " " + ex.getLabel()) + ": ", null).addStyle("font-weight:bold")));
                        c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen")));
                    }
                }
                if (definition.hasMaxLength() && definition.getMaxLength() != 0) {
                    if (!c.getPieces().isEmpty()) {
                        c.addPiece(gen.new Piece("br"));
                    }
                    c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, "Max Length: ", null).addStyle("font-weight:bold")));
                    c.getPieces().add(checkForNoChange(definition.getMaxLengthElement(), gen.new Piece(null, Integer.toString(definition.getMaxLength()), null).addStyle("color: darkgreen")));
                }
                if (profile != null) {
                    for (StructureDefinitionMappingComponent md : profile.getMapping()) {
                        if (md.hasExtension(ToolingExtensions.EXT_TABLE_NAME)) {
                            ElementDefinitionMappingComponent map = null;
                            for (ElementDefinitionMappingComponent m : definition.getMapping()) if (m.getIdentity().equals(md.getIdentity()))
                                map = m;
                            if (map != null) {
                                for (int i = 0; i < definition.getMapping().size(); i++) {
                                    c.addPiece(gen.new Piece("br"));
                                    c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(md, ToolingExtensions.EXT_TABLE_NAME) + ": " + map.getMap(), null));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return c;
}
Also used : ElementDefinitionExampleComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionExampleComponent) StructureDefinition(org.hl7.fhir.r4b.model.StructureDefinition) BindingResolution(org.hl7.fhir.r4b.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution) StructureDefinitionMappingComponent(org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionMappingComponent) Piece(org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece) Cell(org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell) ElementDefinitionBindingComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionBindingComponent) ElementDefinitionConstraintComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionConstraintComponent) ElementDefinitionMappingComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionMappingComponent)

Aggregations

ArrayList (java.util.ArrayList)21 FHIRException (org.hl7.fhir.exceptions.FHIRException)19 DefinitionException (org.hl7.fhir.exceptions.DefinitionException)16 Cell (org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell)16 FHIRFormatError (org.hl7.fhir.exceptions.FHIRFormatError)15 Piece (org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece)14 ElementDefinition (org.hl7.fhir.dstu3.model.ElementDefinition)10 ElementDefinition (org.hl7.fhir.r5.model.ElementDefinition)10 ValidationMessage (org.hl7.fhir.utilities.validation.ValidationMessage)10 ElementDefinition (org.hl7.fhir.r4.model.ElementDefinition)9 CommaSeparatedStringBuilder (org.hl7.fhir.utilities.CommaSeparatedStringBuilder)9 List (java.util.List)8 StructureDefinition (org.hl7.fhir.dstu3.model.StructureDefinition)8 StructureDefinition (org.hl7.fhir.r5.model.StructureDefinition)8 StructureDefinition (org.hl7.fhir.r4.model.StructureDefinition)7 StructureDefinition (org.hl7.fhir.r4b.model.StructureDefinition)7 ElementDefinition (org.hl7.fhir.r4b.model.ElementDefinition)6 StructureDefinition (org.hl7.fhir.dstu2.model.StructureDefinition)5 Color (com.livinglogic.ul4.Color)3 MonthDelta (com.livinglogic.ul4.MonthDelta)3