use of org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent in project org.hl7.fhir.core by hapifhir.
the class ProfileUtilities method findBaseSlices.
private List<BaseTypeSlice> findBaseSlices(StructureDefinitionSnapshotComponent list, int start) {
List<BaseTypeSlice> res = new ArrayList<>();
ElementDefinition base = list.getElement().get(start);
int i = start + 1;
while (i < list.getElement().size() && list.getElement().get(i).getPath().startsWith(base.getPath() + ".")) {
i++;
}
;
while (i < list.getElement().size() && list.getElement().get(i).getPath().equals(base.getPath()) && list.getElement().get(i).hasSliceName()) {
int s = i;
i++;
while (i < list.getElement().size() && list.getElement().get(i).getPath().startsWith(base.getPath() + ".")) {
i++;
}
;
res.add(new BaseTypeSlice(list.getElement().get(s), list.getElement().get(s).getTypeFirstRep().getCode(), s, i - 1));
}
return res;
}
use of org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent in project org.hl7.fhir.core by hapifhir.
the class ProfileUtilities method processPaths.
/**
* @param trimDifferential
* @param srcSD
* @throws DefinitionException, FHIRException
* @throws Exception
*/
private ElementDefinition processPaths(String indent, StructureDefinitionSnapshotComponent result, StructureDefinitionSnapshotComponent base, StructureDefinitionDifferentialComponent differential, int baseCursor, int diffCursor, int baseLimit, int diffLimit, String url, String webUrl, String profileName, String contextPathSrc, String contextPathDst, boolean trimDifferential, String contextName, String resultPathBase, boolean slicingDone, List<ElementRedirection> redirector, StructureDefinition srcSD) throws DefinitionException, FHIRException {
if (debug)
System.out.println(indent + "PP @ " + resultPathBase + " / " + contextPathSrc + " : base = " + baseCursor + " to " + baseLimit + ", diff = " + diffCursor + " to " + diffLimit + " (slicing = " + slicingDone + ", redirector = " + (redirector == null ? "null" : redirector.toString()) + ")");
ElementDefinition res = null;
List<TypeSlice> typeList = new ArrayList<>();
// 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 = fixedPathSource(contextPathSrc, currentBase.getPath(), redirector);
if (debug)
System.out.println(indent + " - " + cpath + ": base = " + baseCursor + " (" + descED(base.getElement(), baseCursor) + ") to " + baseLimit + " (" + descED(base.getElement(), baseLimit) + "), diff = " + diffCursor + " (" + descED(differential.getElement(), diffCursor) + ") to " + diffLimit + " (" + descED(differential.getElement(), diffLimit) + ") " + "(slicingDone = " + slicingDone + ") (diffpath= " + (differential.getElement().size() > diffCursor ? differential.getElement().get(diffCursor).getPath() : "n/a") + ")");
// 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, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
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);
if (hasInnerDiffMatches(differential, cpath, diffCursor, diffLimit, base.getElement(), true)) {
// did we implicitly step into a new type?
if (baseHasChildren(base, currentBase)) {
// not a new type here
processPaths(indent + " ", result, base, differential, baseCursor + 1, diffCursor, baseLimit, diffLimit, url, webUrl, profileName, contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, false, redirector, srcSD);
baseCursor = indexOfFirstNonChild(base, currentBase, baseCursor + 1, baseLimit);
} else {
if (outcome.getType().size() == 0) {
throw new DefinitionException(diffMatches.get(0).getPath() + " has no children (" + differential.getElement().get(diffCursor).getPath() + ") and no types in profile " + profileName);
}
if (outcome.getType().size() > 1) {
for (TypeRefComponent t : outcome.getType()) {
if (!t.getWorkingCode().equals("Reference"))
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("Unknown type " + outcome.getType().get(0) + " at " + diffMatches.get(0).getPath());
contextName = dt.getUrl();
int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), cpath + ".")) diffCursor++;
processPaths(indent + " ", result, dt.getSnapshot(), differential, 1, /* starting again on the data type, but skip the root */
start, dt.getSnapshot().getElement().size() - 1, diffCursor - 1, url, webUrl, profileName, cpath, outcome.getPath(), trimDifferential, contextName, resultPathBase, false, redirector, srcSD);
}
}
baseCursor++;
} else if (diffMatches.size() == 1 && (slicingDone || (!isImplicitSlicing(diffMatches.get(0), cpath) && !(diffMatches.get(0).hasSlicing() || (isExtension(diffMatches.get(0)) && diffMatches.get(0).hasSliceName()))))) {
// 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() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
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(), (sdb.hasUserData("path")) ? Utilities.extractBaseUrl(sdb.getUserString("path")) : webUrl, sd.getName());
}
ElementDefinition src;
if (p.hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) {
src = null;
String eid = p.getExtensionString(ToolingExtensions.EXT_PROFILE_ELEMENT);
for (ElementDefinition t : sd.getSnapshot().getElement()) {
if (eid.equals(t.getId()))
src = t;
}
if (src == null)
throw new DefinitionException("Unable to find element " + eid + " in " + p.getValue());
} else
src = sd.getSnapshot().getElement().get(0);
template = src.copy().setPath(currentBase.getPath());
template.setSliceName(null);
// temporary work around
if (!"Extension".equals(diffMatches.get(0).getType().get(0).getCode())) {
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, webUrl, template);
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
if (res == null)
res = outcome;
updateFromBase(outcome, currentBase);
if (diffMatches.get(0).hasSliceName())
outcome.setSliceName(diffMatches.get(0).getSliceName());
updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url, srcSD);
removeStatusExtensions(outcome);
// if (outcome.getPath().endsWith("[x]") && outcome.getType().size() == 1 && !outcome.getType().get(0).getCode().equals("*") && !diffMatches.get(0).hasSlicing()) // if the base profile allows multiple types, but the profile only allows one, rename it
// outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length()-3)+Utilities.capitalize(outcome.getType().get(0).getCode()));
outcome.setSlicing(null);
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()) || outcome.hasContentReference())) {
// 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() + ".") && !baseWalksInto(base.getElement(), baseCursor)) {
if (outcome.getType().size() > 1) {
if (outcome.getPath().endsWith("[x]") && !diffMatches.get(0).getPath().endsWith("[x]")) {
String en = tail(outcome.getPath());
String tn = tail(diffMatches.get(0).getPath());
String t = tn.substring(en.length() - 3);
if (isPrimitive(Utilities.uncapitalize(t)))
t = Utilities.uncapitalize(t);
// keep any additional information
List<TypeRefComponent> ntr = getByTypeName(outcome.getType(), t);
if (ntr.isEmpty())
ntr.add(new TypeRefComponent().setCode(t));
outcome.getType().clear();
outcome.getType().addAll(ntr);
}
if (outcome.getType().size() > 1)
for (TypeRefComponent t : outcome.getType()) {
if (!t.getCode().equals("Reference")) {
boolean nonExtension = false;
for (ElementDefinition ed : diffMatches) if (ed != diffMatches.get(0) && !ed.getPath().endsWith(".extension"))
nonExtension = true;
if (nonExtension)
throw new DefinitionException(diffMatches.get(0).getPath() + " has children (" + differential.getElement().get(diffCursor).getPath() + ") and multiple types (" + typeCode(outcome.getType()) + ") in profile " + profileName);
}
}
}
int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) diffCursor++;
if (outcome.hasContentReference()) {
ElementDefinition tgt = getElementById(base.getElement(), outcome.getContentReference());
if (tgt == null)
throw new DefinitionException("Unable to resolve reference to " + outcome.getContentReference());
replaceFromContentReference(outcome, tgt);
int nbc = base.getElement().indexOf(tgt) + 1;
int nbl = nbc;
while (nbl < base.getElement().size() && base.getElement().get(nbl).getPath().startsWith(tgt.getPath() + ".")) nbl++;
processPaths(indent + " ", result, base, differential, nbc, start - 1, nbl - 1, diffCursor - 1, url, webUrl, profileName, tgt.getPath(), diffMatches.get(0).getPath(), trimDifferential, contextName, resultPathBase, false, redirectorStack(redirector, outcome, cpath), srcSD);
} else {
StructureDefinition dt = outcome.getType().size() == 1 ? getProfileForDataType(outcome.getType().get(0)) : getProfileForDataType("Element");
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();
processPaths(indent + " ", result, dt.getSnapshot(), differential, 1, /* starting again on the data type, but skip the root */
start, dt.getSnapshot().getElement().size() - 1, diffCursor - 1, url, webUrl, profileName + pathTail(diffMatches, 0), diffMatches.get(0).getPath(), outcome.getPath(), trimDifferential, contextName, resultPathBase, false, new ArrayList<ElementRedirection>(), srcSD);
}
}
}
} else if (diffsConstrainTypes(diffMatches, cpath, typeList)) {
int start = 0;
int nbl = findEndOfElement(base, baseCursor);
int ndc = differential.getElement().indexOf(diffMatches.get(0));
ElementDefinition elementToRemove = null;
// we come here whether they are sliced in the diff, or whether the short cut is used.
if (typeList.get(0).type != null) {
// in R3 (and unpatched R4, as a workaround right now...
if (!FHIRVersion.isR4Plus(context.getVersion()) || !newSlicingProcessing) {
// newSlicingProcessing is a work around for editorial loop dependency
// we insert a cloned element with the right types at the start of the diffMatches
ElementDefinition ed = new ElementDefinition();
ed.setPath(determineTypeSlicePath(diffMatches.get(0).getPath(), cpath));
for (TypeSlice ts : typeList) ed.addType().setCode(ts.type);
ed.setSlicing(new ElementDefinitionSlicingComponent());
ed.getSlicing().addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this");
ed.getSlicing().setRules(SlicingRules.CLOSED);
ed.getSlicing().setOrdered(false);
diffMatches.add(0, ed);
differential.getElement().add(ndc, ed);
elementToRemove = ed;
} else {
// as of R4, this changed; if there's no slice, there's no constraint on the slice types, only one the type.
// so the element we insert specifies no types (= all types) allowed in the base, not just the listed type.
// see also discussion here: https://chat.fhir.org/#narrow/stream/179177-conformance/topic/Slicing.20a.20non-repeating.20element
ElementDefinition ed = new ElementDefinition();
ed.setPath(determineTypeSlicePath(diffMatches.get(0).getPath(), cpath));
ed.setSlicing(new ElementDefinitionSlicingComponent());
ed.getSlicing().addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this");
ed.getSlicing().setRules(SlicingRules.CLOSED);
ed.getSlicing().setOrdered(false);
diffMatches.add(0, ed);
differential.getElement().add(ndc, ed);
elementToRemove = ed;
}
}
int ndl = findEndOfElement(differential, ndc);
// the first element is setting up the slicing
if (diffMatches.get(0).getSlicing().hasRules())
if (diffMatches.get(0).getSlicing().getRules() != SlicingRules.CLOSED)
throw new FHIRException("Error at path " + contextPathSrc + ": Type slicing with slicing.rules != closed");
if (diffMatches.get(0).getSlicing().hasOrdered())
if (diffMatches.get(0).getSlicing().getOrdered())
throw new FHIRException("Error at path " + contextPathSrc + ": Type slicing with slicing.ordered = true");
if (diffMatches.get(0).getSlicing().hasDiscriminator()) {
if (diffMatches.get(0).getSlicing().getDiscriminator().size() != 1)
throw new FHIRException("Error at path " + contextPathSrc + ": Type slicing with slicing.discriminator.count() > 1");
if (!"$this".equals(diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getPath()))
throw new FHIRException("Error at path " + contextPathSrc + ": Type slicing with slicing.discriminator.path != '$this'");
if (diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getType() != DiscriminatorType.TYPE)
throw new FHIRException("Error at path " + contextPathSrc + ": Type slicing with slicing.discriminator.type != 'type'");
}
// check the slice names too while we're at it...
for (TypeSlice ts : typeList) if (ts.type != null) {
String tn = rootName(cpath) + Utilities.capitalize(ts.type);
if (!ts.defn.hasSliceName())
ts.defn.setSliceName(tn);
else if (!ts.defn.getSliceName().equals(tn))
throw new FHIRException("Error at path " + (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath) + ": Slice name must be '" + tn + "' but is '" + ts.defn.getSliceName() + "'");
if (!ts.defn.hasType())
ts.defn.addType().setCode(ts.type);
else if (ts.defn.getType().size() > 1)
throw new FHIRException("Error at path " + (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath) + ": Slice for type '" + tn + "' has more than one type '" + ts.defn.typeSummary() + "'");
else if (!ts.defn.getType().get(0).getCode().equals(ts.type))
throw new FHIRException("Error at path " + (!Utilities.noString(contextPathSrc) ? contextPathSrc : cpath) + ": Slice for type '" + tn + "' has wrong type '" + ts.defn.typeSummary() + "'");
}
// ok passed the checks.
// copy the root diff, and then process any children it has
ElementDefinition e = processPaths(indent + " ", result, base, differential, baseCursor, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, 0), contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, true, redirector, srcSD);
if (e == null)
throw new FHIRException("Did not find type root: " + diffMatches.get(0).getPath());
// now set up slicing on the e (cause it was wiped by what we called.
e.setSlicing(new ElementDefinitionSlicingComponent());
e.getSlicing().addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this");
e.getSlicing().setRules(SlicingRules.CLOSED);
e.getSlicing().setOrdered(false);
start++;
// now we process the base scope repeatedly for each instance of the item in the differential list
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);
processPaths(indent + " ", result, base, differential, baseCursor, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, i), contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, true, redirector, srcSD);
}
if (elementToRemove != null) {
differential.getElement().remove(elementToRemove);
ndl--;
}
// ok, done with that - next in the base list
baseCursor = nbl + 1;
diffCursor = ndl + 1;
} 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.getPath() + " from " + contextName + " in " + url);
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() + "/ (b:" + baseCursor + " of " + baseLimit + " / " + diffCursor + "/ " + diffLimit + ") in profile " + url);
// well, if it passed those preconditions then we slice the dest.
int start = 0;
int nbl = findEndOfElement(base, baseCursor);
// if (diffMatches.size() > 1 && diffMatches.get(0).hasSlicing() && differential.getElement().indexOf(diffMatches.get(1)) > differential.getElement().indexOf(diffMatches.get(0))+1) {
if (diffMatches.size() > 1 && diffMatches.get(0).hasSlicing() && (nbl > baseCursor || differential.getElement().indexOf(diffMatches.get(1)) > differential.getElement().indexOf(diffMatches.get(0)) + 1)) {
// there's a default set before the slices
int ndc = differential.getElement().indexOf(diffMatches.get(0));
int ndl = findEndOfElement(differential, ndc);
ElementDefinition e = processPaths(indent + " ", result, base, differential, baseCursor, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, 0), contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, true, redirector, srcSD);
if (e == null)
throw new FHIRException("Did not find single slice: " + diffMatches.get(0).getPath());
e.setSlicing(diffMatches.get(0).getSlicing());
start++;
} else {
// we're just going to accept the differential slicing at face value
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
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.
if (!diffMatches.get(0).hasSliceName()) {
updateFromDefinition(outcome, diffMatches.get(0), profileName, trimDifferential, url, srcSD);
removeStatusExtensions(outcome);
if (!outcome.hasContentReference() && !outcome.hasType()) {
throw new DefinitionException("not done yet");
}
start++;
// result.getElement().remove(result.getElement().size()-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 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);
/* if (skipSlicingElement && i == 0) {
ndc = ndc + 1;
if (ndc > ndl)
continue;
}*/
// now we process the base scope repeatedly for each instance of the item in the differential list
processPaths(indent + " ", result, base, differential, baseCursor, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, i), contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, true, redirector, srcSD);
}
// 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 its children and siblings
while (baseCursor < base.getElement().size() && base.getElement().get(baseCursor).getPath().startsWith(path)) {
ElementDefinition outcome = updateURLs(url, webUrl, base.getElement().get(baseCursor).copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException("Adding wrong path in profile " + profileName + ": " + 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)
// diffpos++; // if there's a slice on the first, we'll ignore any content it has
ElementDefinitionSlicingComponent dSlice = diffMatches.get(0).getSlicing();
ElementDefinitionSlicingComponent bSlice = currentBase.getSlicing();
if (dSlice.hasOrderedElement() && bSlice.hasOrderedElement() && !orderMatches(dSlice.getOrderedElement(), bSlice.getOrderedElement()))
throw new DefinitionException("Slicing rules on differential (" + summarizeSlicing(dSlice) + ") do not match those on base (" + summarizeSlicing(bSlice) + ") - order @ " + path + " (" + contextName + ")");
if (!discriminatorMatches(dSlice.getDiscriminator(), bSlice.getDiscriminator()))
throw new DefinitionException("Slicing rules on differential (" + summarizeSlicing(dSlice) + ") do not match those on base (" + summarizeSlicing(bSlice) + ") - disciminator @ " + path + " (" + contextName + ")");
if (!ruleMatches(dSlice.getRules(), bSlice.getRules()))
throw new DefinitionException("Slicing rules on differential (" + summarizeSlicing(dSlice) + ") do not match those on base (" + summarizeSlicing(bSlice) + ") - rule @ " + path + " (" + contextName + ")");
}
ElementDefinition outcome = updateURLs(url, webUrl, currentBase.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
updateFromBase(outcome, currentBase);
if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) {
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, srcSD);
removeStatusExtensions(outcome);
} else if (!diffMatches.get(0).hasSliceName())
// because of updateFromDefinition isn't called
diffMatches.get(0).setUserData(GENERATED_IN_SNAPSHOT, outcome);
result.getElement().add(outcome);
if (!diffMatches.get(0).hasSliceName()) {
// it's not real content, just the slice
diffpos++;
}
if (hasInnerDiffMatches(differential, cpath, diffpos, diffLimit, base.getElement(), false)) {
int nbl = findEndOfElement(base, baseCursor);
int ndc = differential.getElement().indexOf(diffMatches.get(0)) + 1;
int ndl = findEndOfElement(differential, ndc);
processPaths(indent + " ", result, base, differential, baseCursor + 1, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, 0), contextPathSrc, contextPathDst, trimDifferential, contextName, resultPathBase, false, null, srcSD);
// throw new Error("Not done yet");
// } else if (currentBase.getType().get(0).getCode().equals("BackboneElement") && diffMatches.size() > 0 && diffMatches.get(0).hasSliceName()) {
} else if (currentBase.getType().get(0).getCode().equals("BackboneElement")) {
// We need to copy children of the backbone element before we start messing around with slices
int nbl = findEndOfElement(base, baseCursor);
for (int i = baseCursor + 1; i <= nbl; i++) {
outcome = updateURLs(url, webUrl, base.getElement().get(i).copy());
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, webUrl, baseItem.copy());
updateFromBase(outcome, currentBase);
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
outcome.setSlicing(null);
if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException("Adding wrong path");
if (diffpos < diffMatches.size() && diffMatches.get(diffpos).getSliceName().equals(outcome.getSliceName())) {
// 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(indent + " ", result, base, differential, baseCursor, ndc, nbl, ndl, url, webUrl, profileName + pathTail(diffMatches, diffpos), contextPathSrc, contextPathDst, closed, contextName, resultPathBase, true, redirector, srcSD);
// ok, done with that - now set the cursors for if this is the end
baseCursor = nbl;
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, webUrl, base.getElement().get(baseCursor).copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
if (!outcome.getPath().startsWith(resultPathBase))
throw new DefinitionException("Adding wrong path");
result.getElement().add(outcome);
baseCursor++;
}
// Lloyd - add this for test T15
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 + ")");
if (diffpos == diffMatches.size()) {
// Lloyd This was causing problems w/ Telus
// diffCursor++;
} else {
while (diffpos < diffMatches.size()) {
ElementDefinition diffItem = diffMatches.get(diffpos);
for (ElementDefinition baseItem : baseMatches) if (baseItem.getSliceName().equals(diffItem.getSliceName()))
throw new DefinitionException("Named items are out of order in the slice");
outcome = updateURLs(url, webUrl, currentBase.copy());
// outcome = updateURLs(url, diffItem.copy());
outcome.setPath(fixedPathDest(contextPathDst, outcome.getPath(), redirector, contextPathSrc));
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, srcSD);
removeStatusExtensions(outcome);
// --- LM Added this
diffCursor = differential.getElement().indexOf(diffItem) + 1;
if (!outcome.getType().isEmpty() && (/*outcome.getType().get(0).getCode().equals("Extension") || */
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 (!baseWalksInto(base.getElement(), baseCursor)) {
if (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) {
if (outcome.getType().size() > 1)
for (TypeRefComponent t : outcome.getType()) {
if (!t.getCode().equals("Reference"))
throw new DefinitionException(diffMatches.get(0).getPath() + " has children (" + differential.getElement().get(diffCursor).getPath() + ") and multiple types (" + typeCode(outcome.getType()) + ") in profile " + profileName);
}
TypeRefComponent t = outcome.getType().get(0);
if (t.getCode().equals("BackboneElement")) {
int baseStart = base.getElement().indexOf(currentBase) + 1;
int baseMax = baseStart + 1;
while (baseMax < base.getElement().size() && base.getElement().get(baseMax).getPath().startsWith(currentBase.getPath() + ".")) baseMax++;
int start = diffCursor;
while (differential.getElement().size() > diffCursor && pathStartsWith(differential.getElement().get(diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) diffCursor++;
processPaths(indent + " ", result, base, differential, baseStart, start - 1, baseMax - 1, diffCursor - 1, url, webUrl, profileName + pathTail(diffMatches, 0), base.getElement().get(0).getPath(), base.getElement().get(0).getPath(), trimDifferential, contextName, resultPathBase, false, redirector, srcSD);
} else {
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(indent + " ", 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, webUrl, profileName + pathTail(diffMatches, 0), diffMatches.get(0).getPath(), outcome.getPath(), trimDifferential, contextName, resultPathBase, false, redirector, srcSD);
}
} else if (outcome.getType().get(0).getCode().equals("Extension")) {
// Force URL to appear if we're dealing with an extension. (This is a kludge - may need to drill down in other cases where we're slicing and the type has a profile declaration that could be setting the fixed value)
StructureDefinition dt = getProfileForDataType(outcome.getType().get(0));
for (ElementDefinition extEd : dt.getSnapshot().getElement()) {
// We only want the children that aren't the root
if (extEd.getPath().contains(".")) {
ElementDefinition extUrlEd = updateURLs(url, webUrl, extEd.copy());
extUrlEd.setPath(fixedPathDest(outcome.getPath(), extUrlEd.getPath(), redirector, null));
// updateFromBase(extUrlEd, currentBase);
markDerived(extUrlEd);
result.getElement().add(extUrlEd);
}
}
}
}
}
// ---
diffpos++;
}
}
baseCursor++;
}
}
}
int i = 0;
for (ElementDefinition e : result.getElement()) {
i++;
if (e.hasMinElement() && e.getMinElement().getValue() == null)
throw new Error("null min");
}
return res;
}
use of org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent 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;
}
use of org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent in project kindling by HL7.
the class ProfileGenerator method generate.
public StructureDefinition generate(PrimitiveType type) throws Exception {
genUml(type);
StructureDefinition p = new StructureDefinition();
p.setId(type.getCode());
p.setUrl("http://hl7.org/fhir/StructureDefinition/" + type.getCode());
p.setKind(StructureDefinitionKind.PRIMITIVETYPE);
p.setAbstract(false);
p.setUserData("filename", type.getCode().toLowerCase());
p.setUserData("path", "datatypes.html#" + type.getCode());
p.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/PrimitiveType");
p.setType(type.getCode());
p.setDerivation(TypeDerivationRule.SPECIALIZATION);
p.setFhirVersion(version);
p.setVersion(version.toCode());
ToolingExtensions.setStandardsStatus(p, StandardsStatus.NORMATIVE, "4.0.0");
ToolResourceUtilities.updateUsage(p, "core");
p.setName(type.getCode());
p.setPublisher("HL7 FHIR Standard");
p.addContact().getTelecom().add(Factory.newContactPoint(ContactPointSystem.URL, "http://hl7.org/fhir"));
p.setDescription("Base StructureDefinition for " + type.getCode() + " Type: " + type.getDefinition());
p.setDate(genDate.getTime());
// normative now
p.setStatus(PublicationStatus.fromCode("active"));
Set<String> containedSlices = new HashSet<String>();
// first, the differential
p.setDifferential(new StructureDefinitionDifferentialComponent());
ElementDefinition ec = new ElementDefinition();
p.getDifferential().getElement().add(ec);
ec.setId(type.getCode());
ec.setPath(type.getCode());
ec.setShort("Primitive Type " + type.getCode());
ec.setDefinition(type.getDefinition());
ec.setComment(type.getComment());
ec.setMin(0);
ec.setMax("*");
ec = new ElementDefinition();
p.getDifferential().getElement().add(ec);
ec.setId(type.getCode() + ".value");
ec.setPath(type.getCode() + ".value");
ec.addRepresentation(PropertyRepresentation.XMLATTR);
ec.setShort("Primitive value for " + type.getCode());
ec.setDefinition("Primitive value for " + type.getCode());
ec.setMin(0);
ec.setMax("1");
TypeRefComponent t = ec.addType();
t.getFormatCommentsPre().add("Note: special primitive values have a FHIRPath system type. e.g. this is compiler magic (a)");
t.setCode(Constants.NS_SYSTEM_TYPE + type.getFHIRPathType());
ToolingExtensions.addUriExtension(t, ToolingExtensions.EXT_FHIR_TYPE, type.getCode());
if (!Utilities.noString(type.getRegex())) {
ToolingExtensions.addStringExtension(t, ToolingExtensions.EXT_REGEX, type.getRegex());
}
addSpecificDetails(type, ec);
reset();
// now. the snapshot
p.setSnapshot(new StructureDefinitionSnapshotComponent());
ElementDefinition ec1 = new ElementDefinition(true, ElementDefinition.NOT_MODIFIER, ElementDefinition.NOT_IN_SUMMARY);
p.getSnapshot().getElement().add(ec1);
ec1.setId(type.getCode());
ec1.setPath(type.getCode());
ec1.setShort("Primitive Type " + type.getCode());
ec1.setDefinition(type.getDefinition());
ec1.setComment(type.getComment());
ec1.setMin(0);
ec1.setMax("*");
ec1.makeBase();
addElementConstraints("Element", ec1);
ElementDefinition ec2 = new ElementDefinition(true, ElementDefinition.NOT_MODIFIER, ElementDefinition.NOT_IN_SUMMARY);
p.getSnapshot().getElement().add(ec2);
ec2.setId(type.getCode() + ".id");
ec2.setPath(type.getCode() + ".id");
ec2.addRepresentation(PropertyRepresentation.XMLATTR);
ec2.setDefinition("unique id for the element within a resource (for internal references)");
ec2.setMin(0);
ec2.setMax("1");
ec2.setShort("xml:id (or equivalent in JSON)");
TypeRefComponent tr = ec2.addType();
t.getFormatCommentsPre().add("Note: special primitive values have a FHIRPath system type. e.g. this is compiler magic (b)");
tr.setCode(Constants.NS_SYSTEM_TYPE + "String");
ToolingExtensions.addUriExtension(tr, ToolingExtensions.EXT_FHIR_TYPE, "string");
generateElementDefinition(p, ec2, ec1);
ec2.makeBase("Element.id", 0, "1");
makeExtensionSlice("extension", p, p.getSnapshot(), null, type.getCode());
ElementDefinition ec3 = new ElementDefinition(true, ElementDefinition.NOT_MODIFIER, ElementDefinition.NOT_IN_SUMMARY);
p.getSnapshot().getElement().add(ec3);
ec3.setId(type.getCode() + ".value");
ec3.setPath(type.getCode() + ".value");
ec3.addRepresentation(PropertyRepresentation.XMLATTR);
ec3.setDefinition("The actual value");
ec3.setMin(0);
ec3.setMax("1");
ec3.setShort("Primitive value for " + type.getCode());
ec3.makeBase();
t = ec3.addType();
t.setCodeElement(new UriType());
t.getFormatCommentsPre().add("Note: special primitive values have a FHIRPath system type. e.g. this is compiler magic (c)");
t.setCode(Constants.NS_SYSTEM_TYPE + type.getFHIRPathType());
ToolingExtensions.addUriExtension(t, ToolingExtensions.EXT_FHIR_TYPE, type.getCode());
if (!Utilities.noString(type.getRegex()))
ToolingExtensions.addStringExtension(t, ToolingExtensions.EXT_REGEX, type.getRegex());
addSpecificDetails(type, ec3);
generateElementDefinition(p, ec3, ec);
containedSlices.clear();
addElementConstraintToSnapshot(p);
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
div.addText("to do");
p.setText(new Narrative());
p.getText().setStatus(NarrativeStatus.GENERATED);
p.getText().setDiv(div);
checkHasTypes(p);
return p;
}
use of org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionSnapshotComponent in project kindling by HL7.
the class ProfileGenerator method generate.
public StructureDefinition generate(Profile pack, ResourceDefn r, String usage, boolean logical) throws Exception {
StructureDefinition p = new StructureDefinition();
p.setId(r.getRoot().getName());
p.setUrl("http://hl7.org/fhir/StructureDefinition/" + r.getRoot().getName());
if (logical)
p.setKind(StructureDefinitionKind.LOGICAL);
else
p.setKind(StructureDefinitionKind.RESOURCE);
if (r.isInterface()) {
ToolingExtensions.addBooleanExtension(p, ToolingExtensions.EXT_RESOURCE_INTERFACE, true);
}
IniFile cini = new IniFile(Utilities.path(rootFolder, "temp", "categories.ini"));
String cat = cini.getStringProperty("category", r.getName());
if (!Utilities.noString(cat))
ToolingExtensions.setStringExtension(p, ToolingExtensions.EXT_RESOURCE_CATEGORY, cat);
p.setAbstract(r.isAbstract());
assert !Utilities.noString(r.getRoot().typeCode());
if (!Utilities.noString(r.getRoot().typeCode())) {
p.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/" + r.getRoot().typeCode());
p.setDerivation(TypeDerivationRule.SPECIALIZATION);
// if (r.getTemplate() != null)
// ToolingExtensions.addStringExtension(p.getBaseDefinitionElement(), ToolingExtensions.EXT_CODE_GENERATION_PARENT, r.getTemplate().getName());
}
p.setType(r.getRoot().getName());
p.setUserData("filename", r.getName().toLowerCase());
p.setUserData("path", r.getName().toLowerCase() + ".html");
p.setTitle(pack.metadata("display"));
p.setFhirVersion(version);
p.setVersion(version.toCode());
ToolingExtensions.setStandardsStatus(p, r.getStatus(), r.getNormativeVersion());
if (r.getFmmLevel() != null)
ToolingExtensions.addIntegerExtension(p, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(r.getFmmLevel()));
if (r.getSecurityCategorization() != null)
ToolingExtensions.addCodeExtension(p, ToolingExtensions.EXT_SEC_CAT, r.getSecurityCategorization().toCode());
ToolResourceUtilities.updateUsage(p, usage);
p.setName(r.getRoot().getName());
p.setPublisher("Health Level Seven International" + (r.getWg() == null ? "" : " (" + r.getWg().getName() + ")"));
p.addContact().getTelecom().add(Factory.newContactPoint(ContactPointSystem.URL, "http://hl7.org/fhir"));
if (r.getWg() != null)
p.addContact().getTelecom().add(Factory.newContactPoint(ContactPointSystem.URL, r.getWg().getUrl()));
ToolingExtensions.setCodeExtension(p, ToolingExtensions.EXT_WORKGROUP, r.getWg().getCode());
p.setDescription(r.getDefinition());
p.setPurpose(r.getRoot().getRequirements());
if (!p.hasPurpose())
p.setPurpose(r.getRoot().getRequirements());
p.setDate(genDate.getTime());
// DSTU
p.setStatus(r.getStatus() == StandardsStatus.NORMATIVE ? PublicationStatus.fromCode("active") : PublicationStatus.fromCode("draft"));
Set<String> containedSlices = new HashSet<String>();
// first, the differential
p.setDifferential(new StructureDefinitionDifferentialComponent());
defineElement(null, p, p.getDifferential().getElement(), r.getRoot(), r.getRoot().getName(), containedSlices, new ArrayList<ProfileGenerator.SliceHandle>(), SnapShotMode.None, true, "BackboneElement", r.getRoot().typeCode(), false);
reset();
// now. the snapshot'
p.setSnapshot(new StructureDefinitionSnapshotComponent());
defineElement(null, p, p.getSnapshot().getElement(), r.getRoot(), r.getRoot().getName(), containedSlices, new ArrayList<ProfileGenerator.SliceHandle>(), SnapShotMode.Resource, true, "BackboneElement", r.getRoot().typeCode(), true);
for (ElementDefinition ed : p.getSnapshot().getElement()) if (!ed.hasBase() && !logical)
generateElementDefinition(p, ed, getParent(ed, p.getSnapshot().getElement()));
if (!logical && !r.isInterface()) {
List<String> names = new ArrayList<String>();
names.addAll(r.getSearchParams().keySet());
Collections.sort(names);
// 1st, non composites
for (String pn : names) {
SearchParameterDefn sp = r.getSearchParams().get(pn);
if (sp.getType() != SearchType.composite)
pack.getSearchParameters().add(makeSearchParam(p, r.getName() + "-" + pn.replace("_", ""), r.getName(), sp, r));
}
for (String pn : names) {
SearchParameterDefn sp = r.getSearchParams().get(pn);
if (sp.getType() == SearchType.composite)
pack.getSearchParameters().add(makeSearchParam(p, r.getName() + "-" + pn.replace("_", ""), r.getName(), sp, r));
}
}
containedSlices.clear();
addElementConstraintToSnapshot(p);
p.getDifferential().getElement().get(0).getType().clear();
p.getSnapshot().getElement().get(0).getType().clear();
XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
div.addText("to do");
p.setText(new Narrative());
p.getText().setStatus(NarrativeStatus.GENERATED);
p.getText().setDiv(div);
checkHasTypes(p);
return p;
}
Aggregations