
001package org.hl7.fhir.r5.conformance.profile; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.Iterator; 006import java.util.List; 007import java.util.Set; 008 009import lombok.extern.slf4j.Slf4j; 010import org.hl7.fhir.exceptions.DefinitionException; 011import org.hl7.fhir.exceptions.FHIRException; 012import org.hl7.fhir.r5.conformance.ElementRedirection; 013import org.hl7.fhir.r5.extensions.ExtensionDefinitions; 014import org.hl7.fhir.r5.model.Base; 015import org.hl7.fhir.r5.model.CanonicalType; 016import org.hl7.fhir.r5.model.ElementDefinition; 017import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; 018import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent; 019import org.hl7.fhir.r5.model.ElementDefinition.SlicingRules; 020import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 021import org.hl7.fhir.r5.model.StructureDefinition; 022import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 023import org.hl7.fhir.r5.utils.UserDataNames; 024import org.hl7.fhir.utilities.*; 025import org.hl7.fhir.utilities.i18n.I18nConstants; 026import org.hl7.fhir.utilities.validation.ValidationMessage; 027import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 028import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 029 030import lombok.AccessLevel; 031import lombok.AllArgsConstructor; 032import lombok.Getter; 033import lombok.With; 034 035@AllArgsConstructor(access = AccessLevel.PRIVATE) 036@MarkedToMoveToAdjunctPackage 037@Slf4j 038public class ProfilePathProcessor { 039 040 /** 041 * The code contains a fix for an issue that was reported where slices that are being defined don't inherit 042 * properties from the slice. This is a reference to this text in the profiling documentation: 043 * 044 * > That is, the first element in a slice group must contain a slicing property that defines the discriminator 045 * > for all members of the group. It also contains the unconstrained definition of the element that is sliced, 046 * > potentially including children of the unconstrained element, if there are any 047 * 048 * APPLY_PROPERTIES_FROM_SLICER = true will mean that this code works that way (though there is some interpretational questions 049 * around the meaning of this text) 050 * 051 * But the community decided not to apply this fix in practice, and to change(/clarify) the text above, so the 052 * fix is not enabled. See discussion at 053 * https://chat.fhir.org/#narrow/channel/179252-IG-creation/topic/Slices.20not.20inheriting.20preferred.20bindings.20from.20root 054 * 055 */ 056 private static final boolean APPLY_PROPERTIES_FROM_SLICER = false; 057 058 059 @Getter 060 protected final ProfileUtilities profileUtilities; 061 062 @Getter 063 @With 064 final String debugIndent; 065 066 @Getter 067 @With 068 final StructureDefinition.StructureDefinitionSnapshotComponent result; 069 070 @Getter 071 @With 072 final StructureDefinition.StructureDefinitionDifferentialComponent differential; 073 074 @Getter 075 @With 076 final int baseLimit; 077 078 @Getter 079 @With 080 final int diffLimit; 081 082 @Getter 083 @With 084 final String url; 085 086 @Getter 087 @With 088 final String webUrl; 089 090 @Getter 091 @With 092 final String profileName; 093 094 @Getter 095 @With 096 final String contextPathSource; 097 098 @Getter 099 @With 100 final String contextPathTarget; 101 102 @Getter 103 @With 104 final boolean trimDifferential; 105 106 @Getter 107 @With 108 final List<ElementRedirection> redirector; 109 110 @Getter 111 @With 112 final StructureDefinition sourceStructureDefinition; 113 114 @Getter 115 @With 116 final StructureDefinition derived; 117 118 @Getter 119 @With 120 final PathSlicingParams slicing; 121 122 123 private ProfilePathProcessor( 124 ProfileUtilities profileUtilities 125 ) { 126 this.profileUtilities = profileUtilities; 127 debugIndent = ""; 128 this.result = null; 129 this.differential = null; 130 this.baseLimit = 0; 131 this.diffLimit = 0; 132 this.url = null; 133 this.webUrl = null; 134 this.profileName = null; 135 this.contextPathSource = null; 136 this.contextPathTarget = null; 137 this.trimDifferential = false; 138 this.redirector = null; 139 this.sourceStructureDefinition = null; 140 this.derived = null; 141 this.slicing = null; 142 } 143 144 public static ProfilePathProcessor getInstance( ProfileUtilities profileUtilities) { 145 return new ProfilePathProcessor(profileUtilities); 146 } 147 148 public ProfilePathProcessor incrementDebugIndent() { 149 return this.withDebugIndent(this.debugIndent + " ".repeat(2)); 150 } 151 152 153 protected static void processPaths(ProfileUtilities profileUtilities, StructureDefinition base, StructureDefinition derived, String url, String webUrl, StructureDefinition.StructureDefinitionDifferentialComponent differential, StructureDefinition.StructureDefinitionSnapshotComponent baseSnapshot, MappingAssistant mapHelper) { 154 155 ProfilePathProcessorState cursors = new ProfilePathProcessorState( 156 base, 157 baseSnapshot, 158 0, 159 0, 160 base.getUrl(), 161 null); 162 163 164 getInstance(profileUtilities) 165 .withResult(derived.getSnapshot()) 166 .withDifferential(differential) 167 .withBaseLimit(baseSnapshot.getElement().size() - 1) 168 .withDiffLimit(differential.hasElement() ? differential.getElement().size() - 1 : -1) 169 .withUrl(url) 170 .withWebUrl(webUrl) 171 .withProfileName(derived.present()) 172 .withContextPathSource(null) 173 .withContextPathTarget(null) 174 .withTrimDifferential(false) 175 .withRedirector(new ArrayList<ElementRedirection>()) 176 .withSourceStructureDefinition(base) 177 .withDerived(derived) 178 .withSlicing(new PathSlicingParams()) 179 .processPaths(cursors, mapHelper, null); 180 181 } 182 183 /** 184 * @param cursors 185 * @param mapHelper 186 * @throws DefinitionException, FHIRException 187 * @throws Exception 188 */ 189 private ElementDefinition processPaths(final ProfilePathProcessorState cursors, MappingAssistant mapHelper, ElementDefinition slicerElement) throws FHIRException { 190 debugProcessPathsEntry(cursors); 191 ElementDefinition res = null; 192 List<TypeSlice> typeList = new ArrayList<>(); 193 boolean first = true; 194 195 // just repeat processing entries until we run out of our allowed scope (1st entry, the allowed scope is all the entries) 196 while (cursors.baseCursor <= getBaseLimit() && cursors.baseCursor < cursors.base.getElement().size()) { 197 // get the current focus of the base, and decide what to do 198 ElementDefinition currentBase = cursors.base.getElement().get(cursors.baseCursor); 199 String currentBasePath = profileUtilities.fixedPathSource(getContextPathSource(), currentBase.getPath(), getRedirector()); 200 201 debugProcessPathsIteration(cursors, currentBasePath); 202 checkDiffAssignedAndCursor(cursors); 203 List<ElementDefinition> diffMatches = profileUtilities.getDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), getProfileName()); // get a list of matching elements in scope 204 205 int dc = cursors.diffCursor; 206 // in the simple case, source is not sliced. 207 if (!currentBase.hasSlicing() || currentBasePath.equals(getSlicing().getPath())) 208 { 209 ElementDefinition currentRes = processSimplePath(currentBase, currentBasePath, diffMatches, typeList, cursors, mapHelper, first ? slicerElement : null); 210 if (res == null) { 211 res = currentRes; 212 } 213 } 214 else { 215 processPathWithSlicedBase(currentBase, currentBasePath, diffMatches, typeList, cursors, mapHelper); 216 } 217 // GDG 28-July 2025. this change is for the sd-nested-ext text case 218 // In general, if there's a diffmatch, then once it's processed, the cursor should advance 219 // to account for it being 'used'. But some of the code paths above don't. And the code paths are 220 // complicated. I wrote this expecting it to blow up some existing test cases, but it didn't. 221 // I think it only matters when there's inner content, and the content is missed because the diffCursor 222 // hasn't increased. But actually, it worked just fine to not change any existing test case, and fix 223 // sd-nested-ext. Now maybe we should chase down each of those code paths, and figure out where they 224 // should increment the diffCursor, but that's pretty difficult. Since this *works*, I'm going with this 225 // but it might be something that needs revisiting if other complex slicing with inner matches arises 226 if (diffMatches.size() > 0) { 227 if (dc == cursors.diffCursor) { 228 cursors.diffCursor = cursors.diffCursor + diffMatches.size(); 229 } 230 } 231 first = false; 232 } 233 234 int i = 0; 235 for (ElementDefinition e : getResult().getElement()) { 236 i++; 237 if (e.hasMinElement() && e.getMinElement().getValue() == null) 238 throw new Error(profileUtilities.getContext().formatMessage(I18nConstants.NULL_MIN)); 239 } 240 return res; 241 } 242 243 private void checkDiffAssignedAndCursor(ProfilePathProcessorState cursors) { 244// int i = 0; 245// List<ElementDefinition> list = getDifferential().getElement(); 246// for (ElementDefinition ed : list) { 247// boolean assigned = ed.hasUserData(UserDataNames.UD_DERIVATION_POINTER); 248// if (i < cursors.diffCursor) { 249// if (!assigned) { 250// throw new Error("what?"); 251// } 252// } else if (i > cursors.diffCursor) { 253// if (assigned) { 254// throw new Error("what!?"); 255// } 256// } 257// i++; 258// } 259 260 } 261 262 private void debugProcessPathsIteration(ProfilePathProcessorState cursors, String currentBasePath) { 263 if (profileUtilities.isDebug()) { 264 log.debug(getDebugIndent() + " - " + currentBasePath + ": "+ 265 "base = " + cursors.baseCursor + " (" + profileUtilities.descED(cursors.base.getElement(), cursors.baseCursor) + ") to " + getBaseLimit() +" (" + profileUtilities.descED(cursors.base.getElement(), getBaseLimit()) + "), "+ 266 "diff = " + cursors.diffCursor + " (" + profileUtilities.descED(getDifferential().getElement(), cursors.diffCursor) + ") to " + getDiffLimit() + " (" + profileUtilities.descED(getDifferential().getElement(), getDiffLimit()) + ") " + 267 "(slicingDone = " + getSlicing().isDone() + ") (diffpath= " + (getDifferential().getElement().size() > cursors.diffCursor ? getDifferential().getElement().get(cursors.diffCursor).getPath() : "n/a") + ")"); 268 String path = cursors.diffCursor >=0 && cursors.diffCursor < getDifferential().getElement().size() ? getDifferential().getElement().get(cursors.diffCursor).present() : null; 269 } 270 271 } 272 273 private void debugProcessPathsEntry(ProfilePathProcessorState cursors) { 274 if (profileUtilities.isDebug()) { 275 log.debug(getDebugIndent() + "PP @ " + cursors.resultPathBase + " / " + getContextPathSource() + " : base = " + cursors.baseCursor + " to " + getBaseLimit() + ", diff = " + cursors.diffCursor + " to " + getDiffLimit() + " (slicing = " + getSlicing().isDone() + ", k " + (getRedirector() == null ? "null" : getRedirector().toString()) + ")"); 276 } 277 } 278 279 280 public ElementDefinition processSimplePath( 281 final ElementDefinition currentBase, 282 final String currentBasePath, 283 final List<ElementDefinition> diffMatches, 284 final List<TypeSlice> typeList, 285 final ProfilePathProcessorState cursors, MappingAssistant mapHelper, ElementDefinition slicerElement) throws FHIRException { 286 ElementDefinition res = null; 287 288 // the differential doesn't say anything about this item 289 // so we just copy it in 290 if (diffMatches.isEmpty()) 291 processSimplePathWithEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors, mapHelper); 292 // one matching element in the differential 293 else if (oneMatchingElementInDifferential(getSlicing().isDone(), currentBasePath, diffMatches)) 294 res = processSimplePathWithOneMatchingElementInDifferential(currentBase, currentBasePath, diffMatches, cursors, mapHelper, slicerElement); 295 else if (profileUtilities.diffsConstrainTypes(diffMatches, currentBasePath, typeList)) 296 processSimplePathWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors, mapHelper); 297 else 298 processSimplePathDefault(currentBase, currentBasePath, diffMatches, cursors, mapHelper); 299 300 301 return res; 302 } 303 304 private void processSimplePathDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) { 305 // ok, the differential slices the item. Let's check our pre-conditions to ensure that this is correct 306 if (!profileUtilities.unbounded(currentBase) && !(profileUtilities.isSlicedToOneOnly(diffMatches.get(0)) || profileUtilities.isTypeSlicing(diffMatches.get(0)))) 307 // you can only slice an element that doesn't repeat if the sum total of your slices is limited to 1 308 // (but you might do that in order to split up constraints by type) 309 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_A_SLICE_AN_ELEMENT_THAT_DOES_NOT_REPEAT__FROM__IN_, currentBase.getPath(), currentBase.getPath(), cursors.contextName, diffMatches.get(0).getId(), profileUtilities.sliceNames(diffMatches))); 310 if (!diffMatches.get(0).hasSlicing() && !profileUtilities.isExtension(currentBase)) // well, the diff has set up a slice, but hasn't defined it. this is an error 311 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.DIFFERENTIAL_DOES_NOT_HAVE_A_SLICE__B_OF_____IN_PROFILE_, currentBase.getPath(), cursors.baseCursor, getBaseLimit(), cursors.diffCursor, getDiffLimit(), getUrl(), currentBasePath)); 312 313 // well, if it passed those preconditions then we slice the dest. 314 int start = 0; 315 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 316// if (diffMatches.size() > 1 && diffMatches.get(0).hasSlicing() && differential.getElement().indexOf(diffMatches.get(1)) > differential.getElement().indexOf(diffMatches.get(0))+1) { 317 ElementDefinition slicerElement; 318 StructureDefinition slicerSource; 319 if (diffMatches.size() > 1 && diffMatches.get(0).hasSlicing() && (newBaseLimit > cursors.baseCursor || (diffMatches.size() > 1 ? getDifferential().getElement().indexOf(diffMatches.get(1)) : -1) > (diffMatches.size() > 0 ? getDifferential().getElement().indexOf(diffMatches.get(0)) : -1) + 1)) { // there's a default set before the slices 320 int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(0)); 321 int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 322 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase); 323 ElementDefinition e = this 324 .incrementDebugIndent() 325 .withBaseLimit(newBaseLimit) 326 .withDiffLimit(newDiffLimit) 327 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)).withSlicing(new PathSlicingParams(true, null, null)) 328 .processPaths(ncursors, mapHelper, null); 329 if (e == null) 330 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_SINGLE_SLICE_, diffMatches.get(0).getPath())); 331 e.setSlicing(diffMatches.get(0).getSlicing()); 332 slicerElement = setPath(e, currentBasePath); 333 start++; 334 } else { 335 // we're just going to accept the differential slicing at face value 336 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); 337 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 338 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 339 340 if (!diffMatches.get(0).hasSlicing()) { 341 outcome.setSlicing(profileUtilities.makeExtensionSlicing()); 342 outcome.setUserData(UserDataNames.SNAPSHOT_auto_added_slicing, true); 343 } else { 344 outcome.setSlicing(diffMatches.get(0).getSlicing().copy()); 345 for (int i = 1; i < diffMatches.size(); i++) { 346 if (diffMatches.get(i).hasSlicing()) { 347 if (!slicingMatches(diffMatches.get(0).getSlicing(), diffMatches.get(i).getSlicing())) { 348 profileUtilities.getMessages().add(new ValidationMessage(Source.InstanceValidator, ValidationMessage.IssueType.BUSINESSRULE, diffMatches.get(0).getPath(), 349 profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_CHANGE_SLICING, diffMatches.get(0).getId(), slicingSummary(diffMatches.get(0).getSlicing()), diffMatches.get(i).getId(), slicingSummary(diffMatches.get(i).getSlicing())), 350 ValidationMessage.IssueSeverity.ERROR)); 351 } else { 352 profileUtilities.getMessages().add(new ValidationMessage(Source.InstanceValidator, ValidationMessage.IssueType.BUSINESSRULE, diffMatches.get(0).getPath(), 353 profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_CHANGE_SLICING, diffMatches.get(0).getId(), diffMatches.get(i).getId()), 354 IssueSeverity.INFORMATION)); 355 356 } 357 } 358 } 359 } 360 if (cursors.resultPathBase != null) { 361 if (!outcome.getPath().startsWith(cursors.resultPathBase)) 362 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 363 } 364 debugCheck(outcome); 365 addToResult(outcome); 366 slicerElement = setPath(outcome, currentBasePath); 367 368 // 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. 369 if (!diffMatches.get(0).hasSliceName()) { 370 profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(),getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper, false); 371 if (!outcome.hasContentReference() && !outcome.hasType() && outcome.getPath().contains(".")) { 372 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.NOT_DONE_YET)); 373 } 374 if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), false)) { 375 if (baseHasChildren(cursors.base, currentBase)) { // not a new type here 376 if (cursors.diffCursor == 0) { 377 throw new DefinitionException("Error: The profile has slicing at the root ('"+currentBase.getPath()+"'), which is illegal"); 378 } else { 379 throw new Error("This situation is not yet handled (constrain slicing to 1..1 and fix base slice for inline structure - please report issue to grahame@fhir.org along with a test case that reproduces this error (@ " + currentBasePath + " | " + currentBase.getPath() + ")"); 380 } 381 } else { 382 StructureDefinition dt = profileUtilities.getTypeForElement(getDifferential(), cursors.diffCursor, getProfileName(), diffMatches, outcome, getWebUrl(), getDerived()); 383 cursors.contextName = dt.getUrl(); 384 cursors.diffCursor++; 385 start = cursors.diffCursor; 386 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) 387 cursors.diffCursor++; 388 cursors.diffCursor--; 389 390 this.incrementDebugIndent() 391 .withBaseLimit( dt.getSnapshot().getElement().size() - 1) 392 .withDiffLimit(cursors.diffCursor) 393 .withWebUrl(profileUtilities.getWebUrl(dt, getWebUrl())) 394 .withContextPathSource(currentBasePath) 395 .withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()) /* starting again on the data type, but skip the root */ 396 . processPaths(new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start, 397 cursors.contextName, cursors.resultPathBase), mapHelper, slicerElement); 398 } 399 } else { 400 if (currentBase.hasContentReference() && !baseHasChildren(cursors.base, currentBase)) { 401 // we have to dump the content of the redirect type inline here at this point. 402 int bstart = resolveContentReference(cursors.base, currentBase); 403 int bend = profileUtilities.findEndOfElementNoSlices(cursors.base, bstart); 404 for (int i = bstart + 1; i <= bend; i++) { 405 ElementDefinition src = cursors.base.getElement().get(i); 406 outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), src.copy(), true); 407 outcome.setId(null); 408 String path = outcome.getPath(); 409 path = fixForRedirect(path, currentBase.getPath(), currentBase.getContentReference().substring(currentBase.getContentReference().indexOf("#")+1)); 410 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), path, getRedirector(), getContextPathSource())); 411 profileUtilities.updateFromBase(outcome, src, getSourceStructureDefinition().getUrl()); 412 profileUtilities.markExtensions(outcome, false, cursors.baseSource); 413 debugCheck(outcome); 414 addToResult(outcome); 415 } 416 } 417 } 418 start++; 419 // result.getElement().remove(result.getElement().size()-1); 420 } else 421 profileUtilities.checkExtensionDoco(outcome); 422 } 423 // now, for each entry in the diff matches, we're going to process the base item 424 // our processing scope for base is all the children of the current path 425 int newDiffCursor = cursors.diffCursor; 426 int newDiffLimit = cursors.diffCursor; 427 for (int i = start; i < diffMatches.size(); i++) { 428 // our processing scope for the differential is the item in the list, and all the items before the next one in the list 429 newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(i)); 430 newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 431 432 // now we process the base scope repeatedly for each instance of the item in the differential list 433 434 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase); 435 this.incrementDebugIndent() 436 .withBaseLimit(newBaseLimit) 437 .withDiffLimit(newDiffLimit) 438 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i)) 439 .withSlicing(new PathSlicingParams(true, slicerElement, null)) 440 .processPaths(ncursors, mapHelper, slicerElement); 441 } 442 // ok, done with that - next in the base list 443 cursors.baseCursor = newBaseLimit + 1; 444 cursors.diffCursor = newDiffLimit + 1; 445 } 446 447 private ElementDefinition setPath(ElementDefinition ed, String path) { 448 ed.setId(null); 449 return ed; 450 } 451 452 private String fixForRedirect(String path, String rootPath, String redirect) { 453 return path.replace(redirect, rootPath); 454 } 455 456 private int resolveContentReference(StructureDefinition.StructureDefinitionSnapshotComponent base, ElementDefinition currentBase) { 457 String path = currentBase.getContentReference().substring(currentBase.getContentReference().indexOf("#")+1); 458 459 // work backwards and find the nearest case 460 int res = base.getElement().indexOf(currentBase) - 1; 461 while (res >= 0) { 462 ElementDefinition ed = base.getElement().get(res); 463 if (path.equals(ed.getPath())) { 464 return res; 465 } 466 res--; 467 } 468 return -1; 469 } 470 471 private String diffPath(ElementDefinition ed) { 472 return "StructureDefinition.differential.element["+differential.getElement().indexOf(ed)+"]"; 473 } 474 475 private String slicingSummary(ElementDefinitionSlicingComponent s) { 476 return s.toString(); 477 } 478 479 private boolean slicingMatches(ElementDefinitionSlicingComponent s1, ElementDefinitionSlicingComponent s2) { 480 if ((!s1.hasOrdered() && s2.hasOrdered()) || (s1.hasOrdered() && s2.hasOrdered() && !Base.compareDeep(s1.getOrderedElement(), s2.getOrderedElement(), false))) { 481 return false; 482 } 483 if ((!s1.hasRules() && s2.hasRules()) || (s1.hasRules() && s2.hasRules() && !Base.compareDeep(s1.getRulesElement(), s2.getRulesElement(), false))) { 484 return false; 485 } 486 return Base.compareDeep(s1.getDiscriminator(), s2.getDiscriminator(), false); 487 } 488 489 private void processSimplePathWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors, MappingAssistant mapHelper) { 490 int start = 0; 491 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 492 int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(0)); 493 ElementDefinition elementToRemove = null; 494 boolean shortCut = !typeList.isEmpty() && typeList.get(0).getType() != null; 495 // we come here whether they are sliced in the diff, or whether the short cut is used. 496 String path = diffMatches.get(0).getPath(); 497 if (shortCut) { 498 // this is the short cut method, we've just dived in and specified a type slice. 499 // in R3 (and unpatched R4, as a workaround right now... 500 if (!VersionUtilities.isR4Plus(profileUtilities.getContext().getVersion()) || !profileUtilities.isNewSlicingProcessing()) { // newSlicingProcessing is a work around for editorial loop dependency 501 // we insert a cloned element with the right types at the start of the diffMatches 502 ElementDefinition ed = new ElementDefinition(); 503 ed.setPath(profileUtilities.determineTypeSlicePath(path, currentBasePath)); 504 for (TypeSlice ts : typeList) 505 ed.addType().setCode(ts.getType()); 506 ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 507 ed.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 508 ed.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); 509 ed.getSlicing().setOrdered(false); 510 diffMatches.add(0, ed); 511 getDifferential().getElement().add(newDiffCursor, ed); 512 elementToRemove = ed; 513 } else { 514 // as of R4, this changed; if there's no slice, there's no constraint on the slice types, only one the type. 515 // so the element we insert specifies no types (= all types) allowed in the base, not just the listed type. 516 // see also discussion here: https://chat.fhir.org/#narrow/stream/179177-conformance/topic/Slicing.20a.20non-repeating.20element 517 ElementDefinition ed = new ElementDefinition(); 518 ed.setPath(profileUtilities.determineTypeSlicePath(path, currentBasePath)); 519 ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 520 ed.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 521 ed.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); 522 ed.getSlicing().setOrdered(false); 523 diffMatches.add(0, ed); 524 getDifferential().getElement().add(newDiffCursor, ed); 525 elementToRemove = ed; 526 } 527 } else { // if it's not a short cut, then the path has to be correct 528 String t1 = currentBasePath.substring(currentBasePath.lastIndexOf(".")+1); 529 String t2 = path.substring(path.lastIndexOf(".")+1); 530 if (!t1.equals(t2)) { 531 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ED_PATH_WRONG_TYPE_MATCH, path.replace(t2, t1), path)); 532 } 533 534 } 535 int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 536 // the first element is setting up the slicing 537 538 if (diffMatches.get(0).getSlicing().hasOrdered()) { 539 if (diffMatches.get(0).getSlicing().getOrdered()) { 540 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGORDERED__TRUE, currentBasePath, getUrl())); 541 } 542 } 543 if (diffMatches.get(0).getSlicing().hasDiscriminator()) { 544 if (diffMatches.get(0).getSlicing().getDiscriminator().size() != 1) { 545 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORCOUNT__1, currentBasePath, getUrl())); 546 } 547 if (diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getType() != ElementDefinition.DiscriminatorType.TYPE) { 548 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORTYPE__TYPE, currentBasePath, getUrl())); 549 } 550 if (!"$this".equals(diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getPath())) { 551 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORPATH__THIS, currentBasePath, getUrl())); 552 } 553 } 554 // check the slice names too while we're at it... 555 for (TypeSlice ts : typeList) { 556 if (ts.getType() != null) { 557 String tn = profileUtilities.rootName(currentBasePath) + Utilities.capitalize(ts.getType()); 558 if (!ts.defn.hasSliceName()) { 559 ts.defn.setSliceName(tn); 560 } else if (!ts.defn.getSliceName().equals(tn)) { 561 if (profileUtilities.isAutoFixSliceNames()) { 562 ts.defn.setSliceName(tn); 563 } else { 564 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_NAME_MUST_BE__BUT_IS_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.getSliceName())); 565 } 566 } 567 if (!ts.defn.hasType()) { 568 ts.defn.addType().setCode(ts.type); 569 } else if (ts.defn.getType().size() > 1) { 570 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_MORE_THAN_ONE_TYPE_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.typeSummary())); 571 } else if (!ts.defn.getType().get(0).getCode().equals(ts.type)) { 572 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_WRONG_TYPE_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.typeSummary())); 573 } 574 } 575 } 576 577 // ok passed the checks. 578 // copy the root diff, and then process any children it has 579 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase); 580 ElementDefinition elementDefinition = 581 this 582 .incrementDebugIndent() 583 .withBaseLimit(newBaseLimit) 584 .withDiffLimit(newDiffLimit) 585 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)) 586 .withSlicing(new PathSlicingParams(true, null, null)) 587 .processPaths(ncursors, mapHelper, null); 588 if (elementDefinition == null) 589 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, path)); 590 // now set up slicing on the e (cause it was wiped by what we called. 591 elementDefinition.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 592 elementDefinition.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 593 elementDefinition.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); // type slicing is always closed; the differential might call it open, but that just means it's not constraining the slices it doesn't mention 594 elementDefinition.getSlicing().setOrdered(false); 595 ElementDefinition slicerElement = elementDefinition.copy(); 596 if (elementDefinition.getType().size() > 1) { 597 slicerElement.setMin(0); 598 } 599 600 start++; 601 602 String fixedType = null; 603 // now process the siblings, which should each be type constrained - and may also have their own children 604 // now we process the base scope repeatedly for each instance of the item in the differential list 605 for (int i = start; i < diffMatches.size(); i++) { 606 // our processing scope for the differential is the item in the list, and all the items before the next one in the list 607 if (diffMatches.get(i).getMin() > 0) { 608 if (diffMatches.size() > i + 1) { 609 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.INVALID_SLICING__THERE_IS_MORE_THAN_ONE_TYPE_SLICE_AT__BUT_ONE_OF_THEM__HAS_MIN__1_SO_THE_OTHER_SLICES_CANNOT_EXIST, diffMatches.get(i).getPath(), diffMatches.get(i).getSliceName())); 610 } else { 611 elementDefinition.setMin(1); 612 } 613 fixedType = profileUtilities.determineFixedType(diffMatches, fixedType, i); 614 } 615 newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(i)); 616 newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 617 ProfilePathProcessorState ncursors2 = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase); 618 ElementDefinition typeSliceElement = 619 this 620 .incrementDebugIndent() 621 .withBaseLimit(newBaseLimit) 622 .withDiffLimit(newDiffLimit) 623 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i)) 624 .withSlicing(new PathSlicingParams(true, elementDefinition, null)) 625 .processPaths(ncursors2, mapHelper, slicerElement); 626 if (typeList.size() > start + 1) { 627 typeSliceElement.setMin(0); 628 } 629 } 630 if (elementToRemove != null) { 631 getDifferential().getElement().remove(elementToRemove); 632 newDiffLimit--; 633 } 634 if (fixedType != null) { 635 for (Iterator<ElementDefinition.TypeRefComponent> iter = elementDefinition.getType().iterator(); iter.hasNext(); ) { 636 ElementDefinition.TypeRefComponent tr = iter.next(); 637 if (!tr.getCode().equals(fixedType)) { 638 iter.remove(); 639 } 640 } 641 } 642 if (!"0".equals(elementDefinition.getMax())) { 643 // check that there's a slice for each allowed types 644 Set<String> allowedTypes = profileUtilities.getListOfTypes(elementDefinition); 645 for (TypeSlice t : typeList) { 646 if (t.type != null) { 647 allowedTypes.remove(t.type); 648 } else if (t.getDefn().hasSliceName() && t.getDefn().getType().size() == 1) { 649 allowedTypes.remove(t.getDefn().getType().get(0).getCode()); 650 } 651 } 652 if (!allowedTypes.isEmpty()) { 653 if (currentBasePath.contains("xtension.value") && shortCut) { 654 for (Iterator<ElementDefinition.TypeRefComponent> iter = elementDefinition.getType().iterator(); iter.hasNext(); ) { 655 ElementDefinition.TypeRefComponent tr = iter.next(); 656 if (allowedTypes.contains(tr.getCode())) { 657 iter.remove(); 658 } 659 } 660 } else { 661 elementDefinition.getSlicing().setRules(ElementDefinition.SlicingRules.OPEN); 662 } 663 } 664 } 665 // ok, done with that - next in the base list 666 cursors.baseCursor = newBaseLimit + 1; 667 cursors.diffCursor = newDiffLimit + 1; 668 } 669 670 private ElementDefinition processSimplePathWithOneMatchingElementInDifferential(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper, ElementDefinition slicerElement) { 671 boolean fromSlicer = slicerElement != null; 672 ElementDefinition res; 673 ElementDefinition template = null; 674 StructureDefinition templateSD = null; 675 if (diffMatches.get(0).hasType() && "Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) && !profileUtilities.isValidType(diffMatches.get(0).getType().get(0), currentBase)) { 676 if (!ProfileUtilities.isSuppressIgnorableExceptions()) { 677 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT, getUrl(), diffMatches.get(0).getPath(), diffMatches.get(0).getType().get(0), currentBase.typeSummary())); 678 } 679 } 680 String id = diffMatches.get(0).getId(); 681 String lid = profileUtilities.tail(id); 682 if (lid.contains("/")) { 683 // the template comes from the snapshot of the base 684 profileUtilities.generateIds(getResult().getElement(), getUrl(), getSourceStructureDefinition().getType(), getSourceStructureDefinition()); 685 String baseId = id.substring(0, id.length() - lid.length()) + lid.substring(0, lid.indexOf("/")); // this is wrong if there's more than one reslice (todo: one thing at a time) 686 template = profileUtilities.getById(getResult().getElement(), baseId); 687 templateSD = getSourceStructureDefinition(); 688 } else if (diffMatches.get(0).hasType() 689 && diffMatches.get(0).getType().size() == 1 690 && diffMatches.get(0).getType().get(0).hasProfile() 691 && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) 692 && !(currentBase.getType().get(0).hasProfile() && currentBase.getType().get(0).getProfile().get(0).primitiveValue().equals(diffMatches.get(0).getType().get(0).getProfile().get(0).primitiveValue()))) { 693 CanonicalType firstTypeProfile = diffMatches.get(0).getType().get(0).getProfile().get(0); 694 StructureDefinition firstTypeStructureDefinition = profileUtilities.getContext().fetchResource(StructureDefinition.class, firstTypeProfile.getValue()); 695 if (firstTypeStructureDefinition == null && profileUtilities.getXver() != null && profileUtilities.getXver().matchingUrl(firstTypeProfile.getValue())) { 696 switch (profileUtilities.getXver().status(firstTypeProfile.getValue())) { 697 case BadVersion: 698 throw new FHIRException("Reference to invalid version in extension url " + firstTypeProfile.getValue()); 699 case Invalid: 700 throw new FHIRException("Reference to invalid extension " + firstTypeProfile.getValue()); 701 case Unknown: 702 throw new FHIRException("Reference to unknown extension " + firstTypeProfile.getValue()); 703 case Valid: 704 firstTypeStructureDefinition = profileUtilities.getXver().getDefinition(firstTypeProfile.getValue()); 705 profileUtilities.generateSnapshot(profileUtilities.getContext().fetchTypeDefinition("Extension"), firstTypeStructureDefinition, firstTypeStructureDefinition.getUrl(), getWebUrl(), firstTypeStructureDefinition.getName()); 706 } 707 } 708 if (firstTypeStructureDefinition != null) { 709 if (!firstTypeStructureDefinition.isGeneratingSnapshot()) { // can't do this check while generating 710 if (!profileUtilities.isMatchingType(firstTypeStructureDefinition, diffMatches.get(0).getType(), firstTypeProfile.getExtensionString(ExtensionDefinitions.EXT_PROFILE_ELEMENT))) { 711 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE2, firstTypeStructureDefinition.getUrl(), diffMatches.get(0).getPath(), firstTypeStructureDefinition.getType(), firstTypeProfile.getValue(), diffMatches.get(0).getType().get(0).getWorkingCode())); 712 } 713 } 714 if (firstTypeStructureDefinition.isGeneratingSnapshot()) { 715 // this is a special case, because we're only going to access the first element, and we can rely on the fact that it's already populated. 716 // but we check anyway 717 if (firstTypeStructureDefinition.getSnapshot().getElementFirstRep().isEmpty()) { 718 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_USE_A_SNAPSHOT_ON_PROFILE__AS__BEFORE_IT_IS_GENERATED, firstTypeStructureDefinition.getUrl(), "Source for first element")); 719 } 720 } else if (!firstTypeStructureDefinition.hasSnapshot()) { 721 StructureDefinition sdb = profileUtilities.getContext().fetchResource(StructureDefinition.class, firstTypeStructureDefinition.getBaseDefinition()); 722 if (sdb == null) 723 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_FIND_BASE__FOR_, firstTypeStructureDefinition.getBaseDefinition(), firstTypeStructureDefinition.getUrl())); 724 profileUtilities.checkNotGenerating(sdb, "an extension base"); 725 profileUtilities.generateSnapshot(sdb, firstTypeStructureDefinition, firstTypeStructureDefinition.getUrl(), (sdb.hasWebPath()) ? Utilities.extractBaseUrl(sdb.getWebPath()) : getWebUrl(), firstTypeStructureDefinition.getName()); 726 } 727 ElementDefinition src; 728 StructureDefinition srcSD = null;; 729 if (firstTypeProfile.hasExtension(ExtensionDefinitions.EXT_PROFILE_ELEMENT)) { 730 src = null; 731 String eid = firstTypeProfile.getExtensionString(ExtensionDefinitions.EXT_PROFILE_ELEMENT); 732 for (ElementDefinition t : firstTypeStructureDefinition.getSnapshot().getElement()) { 733 if (eid.equals(t.getId())) { 734 src = t; 735 srcSD = firstTypeStructureDefinition; 736 } 737 } 738 if (src == null) { 739 if (firstTypeStructureDefinition.isGeneratingSnapshot()) { 740 log.info("At this time the reference to "+eid+" cannot be handled - consult Grahame Grieve"); 741 } else if (Utilities.existsInList(currentBase.typeSummary(), "Extension", "Resource")) { 742 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_FIND_ELEMENT__IN_, eid, firstTypeProfile.getValue())); 743 } 744 } 745 } else { 746 if (firstTypeStructureDefinition.getSnapshot().getElement().isEmpty()) { 747 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, firstTypeStructureDefinition.getVersionedUrl(), "Source for first element")); 748 } else { 749 src = firstTypeStructureDefinition.getSnapshot().getElement().get(0).copy(); 750 srcSD = firstTypeStructureDefinition; 751 if (!src.getPath().contains(".") && firstTypeStructureDefinition.getKind() == StructureDefinitionKind.RESOURCE) { 752 // we can't migrate the constraints in this case, because the sense of %resource changes when the root resource 753 // is treated as an element. The validator will enforce the constraint 754 src.getConstraint().clear(); // 755 } 756 } 757 } 758 if (Utilities.existsInList(currentBase.typeSummary(), "Extension", "Resource")) { 759 template = merge(src, slicerElement).setPath(currentBase.getPath()); 760 templateSD = srcSD; 761 template.setSliceName(null); 762 // temporary work around 763 if (!"Extension".equals(diffMatches.get(0).getType().get(0).getCode())) { 764 template.setMin(currentBase.getMin()); 765 template.setMax(currentBase.getMax()); 766 } 767 } 768 } 769 } 770 if (template == null) { 771 if (!APPLY_PROPERTIES_FROM_SLICER || slicerElement == null || currentBase.hasContentReference()) { 772 template = currentBase.copy(); 773 templateSD = cursors.baseSource; 774 } else { 775 template = slicerElement.copy(); 776 templateSD = cursors.baseSource; 777 template.setPath(currentBase.getPath()); 778 } 779 } else { 780 // some of what's in currentBase overrides template 781 template = profileUtilities.fillOutFromBase(template, APPLY_PROPERTIES_FROM_SLICER && slicerElement != null ? slicerElement : currentBase); 782 } 783 784 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), template, true); 785 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 786 787 res = outcome; 788 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 789 if (diffMatches.get(0).hasSliceName()) { 790 template = merge(currentBase, slicerElement); 791 template = profileUtilities.updateURLs(getUrl(), getWebUrl(), template, true); 792 template.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), template.getPath(), getRedirector(), getContextPathSource())); 793 794 checkToSeeIfSlicingExists(diffMatches.get(0), template); 795 outcome.setSliceName(diffMatches.get(0).getSliceName()); 796 if (!diffMatches.get(0).hasMin() && (diffMatches.size() > 1 || getSlicing().getElementDefinition()== null || getSlicing().getElementDefinition().getSlicing().getRules() != ElementDefinition.SlicingRules.CLOSED) && !currentBase.hasSliceName()) { 797 if (!currentBasePath.endsWith("xtension.value[x]")) { // hack work around for problems with snapshots in official releases 798 outcome.setMin(0); 799 } 800 } 801 } 802 profileUtilities.markExtensions(outcome, false, templateSD); 803 profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper, fromSlicer); 804// 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 805// outcome.setPath(outcome.getPath().substring(0, outcome.getPath().length()-3)+Utilities.capitalize(outcome.getType().get(0).getCode())); 806 if (!APPLY_PROPERTIES_FROM_SLICER && slicerElement != null && outcome.getMaxAsInt() > slicerElement.getMaxAsInt()) { 807 outcome.setMaxElement(slicerElement.getMaxElement()); 808 } 809 outcome.setSlicing(null); 810 if (cursors.resultPathBase == null) 811 cursors.resultPathBase = outcome.getPath(); 812 else if (!outcome.getPath().startsWith(cursors.resultPathBase)) 813 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 814 debugCheck(outcome); 815 addToResult(outcome); 816 cursors.baseCursor++; 817 cursors.diffCursor = getDifferential().getElement().indexOf(diffMatches.get(0)) + 1; 818 if (getDiffLimit() >= cursors.diffCursor && outcome.getPath().contains(".") && (profileUtilities.isDataType(outcome.getType()) || profileUtilities.isBaseResource(outcome.getType()) || outcome.hasContentReference())) { // don't want to do this for the root, since that's base, and we're already processing it 819 if (profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), diffMatches.get(0).getPath() + ".") && !profileUtilities.baseWalksInto(cursors.base.getElement(), cursors.baseCursor)) { 820 if (outcome.getType().size() > 1) { 821 if (outcome.getPath().endsWith("[x]") && !diffMatches.get(0).getPath().endsWith("[x]")) { 822 String en = profileUtilities.tail(outcome.getPath()); 823 String tn = profileUtilities.tail(diffMatches.get(0).getPath()); 824 String t = tn.substring(en.length() - 3); 825 if (profileUtilities.isPrimitive(Utilities.uncapitalize(t))) 826 t = Utilities.uncapitalize(t); 827 List<ElementDefinition.TypeRefComponent> ntr = profileUtilities.getByTypeName(outcome.getType(), t); // keep any additional information 828 if (ntr.isEmpty()) 829 ntr.add(new ElementDefinition.TypeRefComponent().setCode(t)); 830 outcome.getType().clear(); 831 outcome.getType().addAll(ntr); 832 } 833 if (outcome.getType().size() > 1) 834 for (ElementDefinition.TypeRefComponent t : outcome.getType()) { 835 if (!t.getCode().equals("Reference")) { 836 boolean nonExtension = false; 837 for (ElementDefinition ed : diffMatches) 838 if (ed != diffMatches.get(0) && !ed.getPath().endsWith(".extension")) 839 nonExtension = true; 840 if (nonExtension) 841 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, diffMatches.get(0).getPath(), getDifferential().getElement().get(cursors.diffCursor).getPath(), profileUtilities.typeCode(outcome.getType()), getProfileName())); 842 } 843 } 844 } 845 int start = cursors.diffCursor; 846 while (cursors.diffCursor <= getDiffLimit() && getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) 847 cursors.diffCursor++; 848 if (outcome.hasContentReference()) { 849 ProfileUtilities.ElementDefinitionResolution target = profileUtilities.getElementById(getSourceStructureDefinition(), cursors.base.getElement(), outcome.getContentReference()); 850 if (target == null) 851 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_RESOLVE_REFERENCE_TO_, outcome.getContentReference())); 852 profileUtilities.replaceFromContentReference(outcome, target.getElement()); 853 if (target.getSource() != getSourceStructureDefinition()) { 854 cursors.base = target.getSource().getSnapshot(); 855 int newBaseCursor = cursors.base.getElement().indexOf(target.getElement()) + 1; 856 int newBaseLimit = newBaseCursor; 857 while (newBaseLimit < cursors.base.getElement().size() && cursors.base.getElement().get(newBaseLimit).getPath().startsWith(target.getElement().getPath() + ".")) 858 newBaseLimit++; 859 860 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase); 861 this 862 .incrementDebugIndent() 863 .withBaseLimit(newBaseLimit - 1) 864 .withDiffLimit(cursors.diffCursor - 1) 865 .withContextPathSource(target.getElement().getPath()) 866 .withContextPathTarget(diffMatches.get(0).getPath()).withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath)) 867 .withSourceStructureDefinition(target.getSource()) 868 .withSlicing(new PathSlicingParams()) 869 .processPaths(ncursors, mapHelper, null); 870 } else { 871 final int newBaseCursor = cursors.base.getElement().indexOf(target.getElement()) + 1; 872 int newBaseLimit = newBaseCursor; 873 while (newBaseLimit < cursors.base.getElement().size() && cursors.base.getElement().get(newBaseLimit).getPath().startsWith(target.getElement().getPath() + ".")) 874 newBaseLimit++; 875 876 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase); 877 this 878 .incrementDebugIndent() 879 .withBaseLimit(newBaseLimit - 1) 880 .withDiffLimit(cursors.diffCursor - 1) 881 .withContextPathSource(target.getElement().getPath()) 882 .withContextPathTarget(diffMatches.get(0).getPath()) 883 .withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath)) 884 .withSlicing(new PathSlicingParams()) 885 .processPaths(ncursors, mapHelper, null); 886 } 887 } else { 888 StructureDefinition dt = null; 889 TypeRefComponent tr = null; 890 if (outcome.getType().size() > 1) { 891 Set<String> types = new HashSet<>(); 892 for (TypeRefComponent t : outcome.getType()) { 893 types.add(t.getCode()); 894 } 895 if (types.size() == 1) { 896 tr = outcome.getTypeFirstRep(); 897 } else { 898 dt = profileUtilities.getProfileForDataType("Element"); 899 } 900 } else { 901 tr = outcome.getTypeFirstRep(); 902 } 903 if (dt == null) { 904 if (tr.getProfile().size() > 1) { 905 dt = profileUtilities.getContext().fetchTypeDefinition(tr.getWorkingCode()); 906 } else { 907 dt = profileUtilities.getProfileForDataType(tr, getWebUrl(), getDerived()); 908 } 909 } 910 if (dt == null) 911 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE, diffMatches.isEmpty() ? "??" : diffMatches.get(0).getPath(), getDifferential().getElement().get(cursors.diffCursor).getPath(), profileUtilities.typeCode(outcome.getType()), getProfileName())); 912 cursors.contextName = dt.getUrl(); 913 914 this 915 .incrementDebugIndent() 916 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 917 .withDiffLimit(cursors.diffCursor - 1) 918 .withWebUrl( profileUtilities.getWebUrl(dt, getWebUrl())) 919 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)) 920 .withContextPathSource(diffMatches.get(0).getPath()).withContextPathTarget(outcome.getPath()).withRedirector(new ArrayList<ElementRedirection>()) 921 .withSlicing(new PathSlicingParams()). /* starting again on the data type, but skip the root */ 922 processPaths(new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start, 923 cursors.contextName, cursors.resultPathBase), mapHelper, null); 924 } 925 } 926 } 927 return res; 928 } 929 930 private ElementDefinition merge(ElementDefinition src, ElementDefinition slicer) { 931 if (slicer != null && APPLY_PROPERTIES_FROM_SLICER) { 932 ElementDefinition res = slicer.copy(); 933 if (src.getMin() > res.getMin()) { 934 res.setMinElement(src.getMinElement().copy()); 935 } 936 if (src.getMaxAsInt() < res.getMaxAsInt()) { 937 res.setMaxElement(src.getMaxElement()); 938 } 939 return res; 940 } else { 941 return src.copy(); 942 } 943 } 944 945 private void checkToSeeIfSlicingExists(ElementDefinition ed, ElementDefinition template) { 946 List<ElementDefinition> ss = result.getElement(); 947 int i = ss.size() -1; 948 ElementDefinition m = null; 949 950 while (i >= 0) { 951 ElementDefinition t = ss.get(i); 952 if (pathsMatch(t.getPath(), ed.getPath())) { 953 if (t.hasSlicing() || t.hasSliceName() || t.getPath().endsWith("[x]")) { 954 m = t; 955 break; 956 } 957 } 958 if (t.getPath().length() < ed.getPath().length()) { 959 break; 960 } 961 i--; 962 } 963 if (m == null) { 964 if (template.getPath().endsWith(".extension")) { 965 template.getSlicing().setRules(SlicingRules.OPEN); 966 template.getSlicing().setOrdered(false); 967 template.getSlicing().addDiscriminator().setType(DiscriminatorType.VALUE).setPath("url"); 968 addToResult(template); 969 } else if (isJumpingIntoTypeSlicing(ed, template)) { 970 template.getSlicing().setRules(SlicingRules.CLOSED); 971 template.getSlicing().setOrdered(false); 972 template.getSlicing().addDiscriminator().setType(DiscriminatorType.TYPE).setPath("$this"); 973 addToResult(template); 974// log.error("checkToSeeIfSlicingExists: "+ed.getPath()+":"+ed.getSliceName()+" is not sliced"); 975 } 976 } 977 } 978 979 private boolean isJumpingIntoTypeSlicing(ElementDefinition ed, ElementDefinition template) { 980 return template.getPath().endsWith("[x]") && !template.hasSliceName() && !template.hasSlicing() && ed.hasSliceName() && ed.getType().size() > 0; 981 } 982 983 private boolean pathsMatch(String path1, String path2) { 984 String[] p1 = path1.split("\\."); 985 String[] p2 = path2.split("\\."); 986 if (p1.length != p2.length) { 987 return false; 988 } 989 for (int i = 0; i < p1.length; i++) { 990 String pp1 = p1[i]; 991 String pp2 = p2[i]; 992 if (!pp1.equals(pp2)) { 993 if (pp1.endsWith("[x]")) { 994 if (!pp2.startsWith(pp1.substring(0, pp1.length()-3))) { 995 return false; 996 } 997 } else if (pp2.endsWith("[x]")) { 998 if (!pp1.startsWith(pp2.substring(0, pp2.length()-3))) { 999 return false; 1000 } 1001 1002 } else { 1003 return false; 1004 } 1005 } 1006 } 1007 return true; 1008 } 1009 1010 private int indexOfFirstNonChild(StructureDefinition.StructureDefinitionSnapshotComponent base, ElementDefinition currentBase, int i, int baseLimit) { 1011 return baseLimit+1; 1012 } 1013 1014 private boolean baseHasChildren(StructureDefinition.StructureDefinitionSnapshotComponent base, ElementDefinition ed) { 1015 int index = base.getElement().indexOf(ed); 1016 if (index == -1 || index >= base.getElement().size()-1) 1017 return false; 1018 String p = base.getElement().get(index+1).getPath(); 1019 return isChildOf(p, ed.getPath()); 1020 } 1021 1022 1023 private boolean isChildOf(String sub, String focus) { 1024 if (focus.endsWith("[x]")) { 1025 focus = focus.substring(0, focus.length()-3); 1026 return sub.startsWith(focus); 1027 } else 1028 return sub.startsWith(focus+"."); 1029 } 1030 1031 1032 private void processSimplePathWithEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, MappingAssistant mapHelper) { 1033 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); 1034 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1035 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 1036 profileUtilities.updateConstraintSources(outcome, getSourceStructureDefinition().getUrl()); 1037 profileUtilities.checkExtensions(outcome); 1038 profileUtilities.markExtensions(outcome, false, cursors.baseSource); 1039 profileUtilities.updateFromObligationProfiles(outcome); 1040 profileUtilities.updateURLs(url, webUrl, outcome, true); 1041 profileUtilities.markDerived(outcome); 1042 if (cursors.resultPathBase == null) 1043 cursors.resultPathBase = outcome.getPath(); 1044 else if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1045 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH__OUTCOMEGETPATH___RESULTPATHBASE__, outcome.getPath(), cursors.resultPathBase)); 1046 debugCheck(outcome); 1047 addToResult(outcome); 1048 if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) { 1049 // well, the profile walks into this, so we need to as well 1050 // did we implicitly step into a new type? 1051 if (baseHasChildren(cursors.base, currentBase)) { // not a new type here 1052 1053 this.incrementDebugIndent() 1054 .withSlicing(new PathSlicingParams()) 1055 .processPaths(new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase), mapHelper, null); 1056 cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor + 1, getBaseLimit()); 1057 } 1058 else { 1059 if (outcome.getType().size() == 0 && !outcome.hasContentReference()) { 1060 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_NO_CHILDREN__AND_NO_TYPES_IN_PROFILE_, currentBasePath, getDifferential().getElement().get(cursors.diffCursor).getPath(), getProfileName())); 1061 } 1062 boolean nonExtension = false; 1063 if (outcome.getType().size() > 1) { 1064 for (ElementDefinition.TypeRefComponent t : outcome.getType()) { 1065 if (!t.getWorkingCode().equals("Reference")) { 1066 for (ElementDefinition ed : diffMatches) { 1067 if (ed != diffMatches.get(0) && !ed.getPath().endsWith(".extension")) { 1068 nonExtension = true; 1069 } 1070 } 1071 } 1072 } 1073 } 1074 if (!profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) { 1075 cursors.diffCursor++; 1076 } 1077 int start = cursors.diffCursor; 1078 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) 1079 cursors.diffCursor++; 1080 if (nonExtension) { 1081 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, currentBasePath, getDifferential().getElement().get(cursors.diffCursor).getPath(), profileUtilities.typeCode(outcome.getType()), getProfileName())); 1082 } 1083 if (outcome.hasContentReference()) { 1084 ProfileUtilities.ElementDefinitionResolution tgt = profileUtilities.getElementById(getSourceStructureDefinition(), cursors.base.getElement(), outcome.getContentReference()); 1085 if (tgt == null) 1086 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNABLE_TO_RESOLVE_REFERENCE_TO_, outcome.getContentReference())); 1087 profileUtilities.replaceFromContentReference(outcome, tgt.getElement()); 1088 if (tgt.getSource() != getSourceStructureDefinition()) { 1089 cursors.base = tgt.getSource().getSnapshot(); 1090 int newBaseCursor = cursors.base.getElement().indexOf(tgt.getElement()) + 1; 1091 int newBaseLimit = newBaseCursor; 1092 while (newBaseLimit < cursors.base.getElement().size() && cursors.base.getElement().get(newBaseLimit).getPath().startsWith(tgt.getElement().getPath() + ".")) 1093 newBaseLimit++; 1094 1095 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, newBaseCursor, start - 1, cursors.contextName, cursors.resultPathBase); 1096 this 1097 .incrementDebugIndent() 1098 .withBaseLimit(newBaseLimit - 1) 1099 .withDiffLimit(cursors.diffCursor - 1) 1100 .withContextPathSource(tgt.getElement().getPath()) 1101 .withContextPathTarget(diffMatches.get(0).getPath()) 1102 .withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath)) 1103 .withSourceStructureDefinition(tgt.getSource()) 1104 .withSlicing(new PathSlicingParams()) 1105 .processPaths(ncursors, mapHelper, null); 1106 } else { 1107 int newBaseCursor = cursors.base.getElement().indexOf(tgt.getElement()) + 1; 1108 int newBaseLimit = newBaseCursor; 1109 while (newBaseLimit < cursors.base.getElement().size() && cursors.base.getElement().get(newBaseLimit).getPath().startsWith(tgt.getElement().getPath() + ".")) 1110 newBaseLimit++; 1111 1112 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(cursors.baseSource, cursors.base, newBaseCursor, start, cursors.contextName, cursors.resultPathBase); 1113 this 1114 .incrementDebugIndent() 1115 .withBaseLimit(newBaseLimit - 1) 1116 .withDiffLimit(cursors.diffCursor - 1) 1117 .withContextPathSource(tgt.getElement().getPath()) 1118 .withContextPathTarget(outcome.getPath()) 1119 .withRedirector(profileUtilities.redirectorStack(getRedirector(), outcome, currentBasePath)).withSlicing(new PathSlicingParams()) 1120 .processPaths(ncursors, mapHelper, null); 1121 } 1122 } else { 1123 StructureDefinition dt = outcome.getType().size() > 1 ? profileUtilities.getContext().fetchTypeDefinition("Element") : profileUtilities.getProfileForDataType(outcome.getType().get(0), getWebUrl(), getDerived()); 1124 if (dt == null) { 1125 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), currentBasePath)); 1126 } 1127 cursors.contextName = dt.getUrl(); 1128 if (getRedirector() == null || getRedirector().isEmpty()) { 1129 /* starting again on the data type, but skip the root */ 1130 ProfilePathProcessorState ncursors = new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start, 1131 cursors.contextName, cursors.resultPathBase); 1132 this 1133 .incrementDebugIndent() 1134 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 1135 .withDiffLimit(cursors.diffCursor - 1) 1136 .withWebUrl(profileUtilities.getWebUrl(dt, getWebUrl())) 1137 .withContextPathSource(currentBasePath) 1138 .withContextPathTarget(outcome.getPath()) 1139 .withSlicing(new PathSlicingParams()) 1140 .processPaths(ncursors, mapHelper, null); 1141 } else { 1142 ProfilePathProcessorState ncursors2 = /* starting again on the data type, but skip the root */ 1143 new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start, 1144 cursors.contextName, cursors.resultPathBase); 1145 this 1146 .incrementDebugIndent() 1147 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 1148 .withDiffLimit(cursors.diffCursor - 1) 1149 .withWebUrl(profileUtilities.getWebUrl(dt, getWebUrl())) 1150 .withContextPathSource(currentBasePath) 1151 .withContextPathTarget( outcome.getPath()) 1152 .withRedirector(profileUtilities.redirectorStack(getRedirector(), currentBase, currentBasePath)).withSlicing(new PathSlicingParams()) 1153 .processPaths(ncursors2, mapHelper, null); 1154 } 1155 } 1156 } 1157 } 1158 cursors.baseCursor++; 1159 } 1160 1161 private void processPathWithSlicedBase( 1162 ElementDefinition currentBase, 1163 String currentBasePath, 1164 List<ElementDefinition> diffMatches, List<TypeSlice> typeList, 1165 final ProfilePathProcessorState cursors, MappingAssistant mapHelper 1166 ) { 1167 // the item is already sliced in the base profile. 1168 // here's the rules 1169 // 1. irrespective of whether the slicing is ordered or not, the definition order must be maintained 1170 // 2. slice element names have to match. 1171 // 3. new slices must be introduced at the end 1172 // corallory: you can't re-slice existing slices. is that ok? 1173 1174 // we're going to need this: 1175 String path = currentBase.getPath(); 1176 1177 if (diffMatches.isEmpty()) { 1178 processPathWithSlicedBaseAndEmptyDiffMatches(currentBase, currentBasePath, diffMatches, cursors, path, mapHelper); 1179 } 1180 else if (profileUtilities.diffsConstrainTypes(diffMatches, currentBasePath, typeList)) 1181 { 1182 processPathWithSlicedBaseWhereDiffsConstrainTypes(currentBasePath, diffMatches, typeList, cursors, mapHelper); 1183 } 1184 else 1185 { 1186 processPathWithSlicedBaseDefault(currentBase, currentBasePath, diffMatches, cursors, path, mapHelper); 1187 } 1188 } 1189 1190 private void processPathWithSlicedBaseDefault(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path, MappingAssistant mapHelper) { 1191 // first - check that the slicing is ok 1192 boolean closed = currentBase.getSlicing().getRules() == ElementDefinition.SlicingRules.CLOSED; 1193 int diffpos = 0; 1194 if (diffMatches.get(0).hasSlicing()) { // it might be null if the differential doesn't want to say anything about slicing 1195// if (!isExtension) 1196// diffpos++; // if there's a slice on the first, we'll ignore any content it has 1197 ElementDefinition.ElementDefinitionSlicingComponent dSlice = diffMatches.get(0).getSlicing(); 1198 ElementDefinition.ElementDefinitionSlicingComponent bSlice = currentBase.getSlicing(); 1199 if (dSlice.hasOrderedElement() && bSlice.hasOrderedElement() && !profileUtilities.orderMatches(dSlice.getOrderedElement(), bSlice.getOrderedElement())) 1200 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.SLICING_RULES_ON_DIFFERENTIAL__DO_NOT_MATCH_THOSE_ON_BASE___ORDER___, profileUtilities.summarizeSlicing(dSlice), profileUtilities.summarizeSlicing(bSlice), path, cursors.contextName)); 1201 if (!profileUtilities.discriminatorMatches(dSlice.getDiscriminator(), bSlice.getDiscriminator())) 1202 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.SLICING_RULES_ON_DIFFERENTIAL__DO_NOT_MATCH_THOSE_ON_BASE___DISCIMINATOR___, profileUtilities.summarizeSlicing(dSlice), profileUtilities.summarizeSlicing(bSlice), path, url)); 1203 if (!currentBase.isChoice() && !profileUtilities.ruleMatches(dSlice.getRules(), bSlice.getRules())) 1204 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.SLICING_RULES_ON_DIFFERENTIAL__DO_NOT_MATCH_THOSE_ON_BASE___RULE___, profileUtilities.summarizeSlicing(dSlice), profileUtilities.summarizeSlicing(bSlice), path, cursors.contextName)); 1205 } 1206 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); 1207 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1208 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 1209 if (diffMatches.get(0).hasSlicing() || !diffMatches.get(0).hasSliceName()) { 1210 profileUtilities.updateFromSlicing(outcome.getSlicing(), diffMatches.get(0).getSlicing()); 1211 profileUtilities.updateFromDefinition(outcome, diffMatches.get(0), getProfileName(), closed, getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffMatches.get(0)), mapHelper, false); // if there's no slice, we don't want to update the unsliced description 1212 } else if (!diffMatches.get(0).hasSliceName()) { 1213 diffMatches.get(0).setUserData(UserDataNames.SNAPSHOT_GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called 1214 } else { 1215 outcome.setUserData(UserDataNames.SNAPSHOT_auto_added_slicing, true); 1216 } 1217 1218 ProfileUtilities.markExtensions(outcome, false, cursors.baseSource); 1219 debugCheck(outcome); 1220 addToResult(outcome); 1221 1222 if (!diffMatches.get(0).hasSliceName()) { // it's not real content, just the slice 1223 diffpos++; 1224 } 1225 if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), false)) { 1226 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 1227 int ndx = getDifferential().getElement().indexOf(diffMatches.get(0)); 1228 int newDiffCursor = ndx + (diffMatches.get(0).hasSlicing() ? 1 : 0); 1229 int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), ndx); 1230 if (newBaseLimit == cursors.baseCursor) { 1231 if (cursors.base.getElement().get(cursors.baseCursor).getType().size() != 1) { 1232 throw new Error(profileUtilities.getContext().formatMessage(I18nConstants.DIFFERENTIAL_WALKS_INTO____BUT_THE_BASE_DOES_NOT_AND_THERE_IS_NOT_A_SINGLE_FIXED_TYPE_THE_TYPE_IS__THIS_IS_NOT_HANDLED_YET, currentBasePath, diffMatches.get(0).toString(), cursors.base.getElement().get(cursors.baseCursor).typeSummary())); 1233 } 1234 StructureDefinition dt = profileUtilities.getProfileForDataType(cursors.base.getElement().get(cursors.baseCursor).getType().get(0), getWebUrl(), getDerived()); 1235 if (dt == null) { 1236 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.UNKNOWN_TYPE__AT_, outcome.getType().get(0), diffMatches.get(0).getPath())); 1237 } 1238 cursors.contextName = dt.getUrl(); 1239 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) 1240 cursors.diffCursor++; 1241 ProfilePathProcessorState nc = new ProfilePathProcessorState(dt, dt.getSnapshot(), 1, newDiffCursor, cursors.contextName, cursors.resultPathBase); 1242 this 1243 .incrementDebugIndent() 1244 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 1245 .withDiffLimit(newDiffLimit) 1246 .withWebUrl(profileUtilities.getWebUrl(dt, getWebUrl())) 1247 .withContextPathSource(currentBasePath).withContextPathTarget(outcome.getPath()) 1248 .withSlicing(new PathSlicingParams()) 1249 .processPaths(nc, mapHelper, null); 1250 } else { 1251 ProfilePathProcessorState nc = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor + 1, newDiffCursor, 1252 cursors.contextName, cursors.resultPathBase); 1253 this 1254 .incrementDebugIndent() 1255 .withBaseLimit(newBaseLimit) 1256 .withDiffLimit(newDiffLimit) 1257 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)) 1258 .withRedirector(null).withSlicing(new PathSlicingParams()) 1259 .processPaths(nc, mapHelper, null); 1260 } 1261// throw new Error("Not done yet"); 1262// } else if (currentBase.getType().get(0).getCode().equals("BackboneElement") && diffMatches.size() > 0 && diffMatches.get(0).hasSliceName()) { 1263 } else if (!currentBase.getType().isEmpty() && currentBase.getType().get(0).getCode().equals("BackboneElement")) { 1264 // We need to copy children of the backbone element before we start messing around with slices 1265 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 1266 for (int i = cursors.baseCursor + 1; i <= newBaseLimit; i++) { 1267 outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(i).copy(), true); 1268 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1269 profileUtilities.markExtensions(outcome, false, sourceStructureDefinition); 1270 debugCheck(outcome); 1271 addToResult(outcome); 1272 } 1273 } 1274 1275 // now, we have two lists, base and diff. we're going to work through base, looking for matches in diff. 1276 List<ElementDefinition> baseMatches = profileUtilities.getSiblings(cursors.base.getElement(), currentBase); 1277 for (ElementDefinition baseItem : baseMatches) { 1278 cursors.baseCursor = cursors.base.getElement().indexOf(baseItem); 1279 outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), baseItem.copy(), true); 1280 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 1281 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1282 outcome.setSlicing(null); 1283 profileUtilities.markExtensions(outcome, false, sourceStructureDefinition); 1284 if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1285 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 1286 if (diffpos < diffMatches.size() && diffMatches.get(diffpos).hasSliceName() && diffMatches.get(diffpos).getSliceName().equals(outcome.getSliceName())) { 1287 // if there's a diff, we update the outcome with diff 1288 // no? updateFromDefinition(outcome, diffMatches.get(diffpos), profileName, closed, url); 1289 //then process any children 1290 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 1291 int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(diffpos)); 1292 int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 1293 // now we process the base scope repeatedly for each instance of the item in the differential list 1294 ProfilePathProcessorState nc = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, cursors.contextName, cursors.resultPathBase); 1295 this 1296 .incrementDebugIndent() 1297 .withBaseLimit(newBaseLimit) 1298 .withDiffLimit(newDiffLimit) 1299 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, diffpos)) 1300 .withTrimDifferential(closed) 1301 .withSlicing(new PathSlicingParams(true, null, null)) 1302 .processPaths(nc, mapHelper, null); 1303 // ok, done with that - now set the cursors for if this is the end 1304 cursors.baseCursor = newBaseLimit; 1305 cursors.diffCursor = newDiffLimit + 1; 1306 diffpos++; 1307 } else { 1308 debugCheck(outcome); 1309 addToResult(outcome); 1310 cursors.baseCursor++; 1311 // just copy any children on the base 1312 while (cursors.baseCursor < cursors.base.getElement().size() && cursors.base.getElement().get(cursors.baseCursor).getPath().startsWith(path) && !cursors.base.getElement().get(cursors.baseCursor).getPath().equals(path)) { 1313 outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy(), true); 1314 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1315 if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1316 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 1317 outcome.setUserData(UserDataNames.SNAPSHOT_BASE_PATH, outcome.getPath()); 1318 outcome.setUserData(UserDataNames.SNAPSHOT_BASE_MODEL, getSourceStructureDefinition().getUrl()); 1319 profileUtilities.markExtensions(outcome, false, sourceStructureDefinition); 1320 debugCheck(outcome); 1321 addToResult(outcome); 1322 cursors.baseCursor++; 1323 } 1324 //Lloyd - add this for test T15 1325 cursors.baseCursor--; 1326 } 1327 } 1328 // finally, we process any remaining entries in diff, which are new (and which are only allowed if the base wasn't closed 1329 if (closed && diffpos < diffMatches.size()) { 1330 // this is a problem, unless we're on a polymorhpic type and we're going to constrain a slice that actually implicitly exists 1331 if (!currentBase.getPath().endsWith("[x]")) { 1332 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.THE_BASE_SNAPSHOT_MARKS_A_SLICING_AS_CLOSED_BUT_THE_DIFFERENTIAL_TRIES_TO_EXTEND_IT_IN__AT__, getProfileName(), path, currentBasePath)); 1333 } 1334 } 1335 if (diffpos != diffMatches.size()) { 1336 while (diffpos < diffMatches.size()) { 1337 ElementDefinition diffItem = diffMatches.get(diffpos); 1338 for (ElementDefinition baseItem : baseMatches) 1339 if (baseItem.getSliceName().equals(diffItem.getSliceName())) 1340 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.NAMED_ITEMS_ARE_OUT_OF_ORDER_IN_THE_SLICE)); 1341 1342 String id = diffItem.getId(); 1343 String lid = profileUtilities.tail(id); 1344 ElementDefinition template = currentBase; 1345 if (lid.contains("/")) { 1346 profileUtilities.generateIds(getResult().getElement(), getUrl(), getSourceStructureDefinition().getType(), getSourceStructureDefinition()); 1347 String baseId = id.substring(0, id.length() - lid.length()) + lid.substring(0, lid.indexOf("/")); // this is wrong if there's more than one reslice (todo: one thing at a time) 1348 template = profileUtilities.getById(getResult().getElement(), baseId); 1349 } 1350 outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), template.copy(), true); 1351 // outcome = updateURLs(url, diffItem.copy()); 1352 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1353 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 1354 outcome.setSlicing(null); 1355 outcome.setMin(0); // we're in a slice, so it's only a mandatory if it's explicitly marked so 1356 if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1357 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 1358 ProfileUtilities.markExtensions(outcome, false, sourceStructureDefinition); 1359 debugCheck(outcome); 1360 addToResult(outcome); 1361 profileUtilities.updateFromDefinition(outcome, diffItem, getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived(), diffPath(diffItem), mapHelper, false); 1362 1363 // do we need to pick up constraints from the type? 1364 List<String> profiles = new ArrayList<>(); 1365 for (TypeRefComponent tr : outcome.getType()) { 1366 for (CanonicalType ct : tr.getProfile()) { 1367 profiles.add(ct.getValueAsString()); 1368 } 1369 } 1370 if (profiles.size() == 1) { 1371 StructureDefinition sdt = profileUtilities.getContext().fetchResource(StructureDefinition.class, profiles.get(0)); 1372 if (sdt != null) { 1373 ElementDefinition edt = sdt.getSnapshot().getElementFirstRep(); 1374 if (edt.isMandatory() && !outcome.isMandatory()) { 1375 outcome.setMin(edt.getMin()); 1376 } 1377 if (!edt.repeats() && outcome.repeats()) { 1378 outcome.setMax(edt.getMax()); 1379 } 1380 // todo: should we consider other constraints? 1381 // throw new Error("Not handled yet: "+sdt.getVersionedUrl()+" / "+outcome.getPath()+":"+outcome.getSliceName()); 1382 } 1383 } else if (profiles.size() > 1) { 1384 throw new Error("Not handled: multiple profiles at "+outcome.getPath()+":"+outcome.getSliceName()+": "+CommaSeparatedStringBuilder.join(",", profiles)); 1385 } 1386 cursors.diffCursor = getDifferential().getElement().indexOf(diffItem) + 1; 1387 if ((!outcome.getType().isEmpty()) && (/*outcome.getType().get(0).getCode().equals("Extension") || */getDifferential().getElement().size() > cursors.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 1388 if (!profileUtilities.baseWalksInto(cursors.base.getElement(), cursors.baseCursor)) { 1389 if (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) { 1390 if (outcome.getType().size() > 1) 1391 for (ElementDefinition.TypeRefComponent t : outcome.getType()) { 1392 if (!t.getCode().equals("Reference")) 1393 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_CHILDREN__AND_MULTIPLE_TYPES__IN_PROFILE_, diffMatches.get(0).getPath(), getDifferential().getElement().get(cursors.diffCursor).getPath(), ProfileUtilities.typeCode(outcome.getType()), getProfileName())); 1394 } 1395 ElementDefinition.TypeRefComponent t = outcome.getType().get(0); 1396 if (Utilities.existsInList(t.getCode(), "Base", "Element", "BackboneElement")) { 1397 int baseStart = cursors.base.getElement().indexOf(currentBase) + 1; 1398 int baseMax = baseStart + 1; 1399 while (baseMax < cursors.base.getElement().size() && cursors.base.getElement().get(baseMax).getPath().startsWith(currentBase.getPath() + ".")) 1400 baseMax++; 1401 int start = cursors.diffCursor; 1402 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) 1403 cursors.diffCursor++; 1404 ProfilePathProcessorState nc = new ProfilePathProcessorState(cursors.baseSource, cursors.base, baseStart, start - 1, 1405 cursors.contextName, cursors.resultPathBase); 1406 this.incrementDebugIndent().withBaseLimit(baseMax - 1) 1407 .withDiffLimit(cursors.diffCursor - 1) 1408 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)) 1409 .withContextPathSource(cursors.base.getElement().get(0).getPath()) 1410 .withContextPathTarget(cursors.base.getElement().get(0).getPath()) 1411 .withSlicing(new PathSlicingParams()) 1412 .processPaths(nc, mapHelper, null); 1413 } else { 1414 StructureDefinition dt = profileUtilities.getProfileForDataType(outcome.getType().get(0), getWebUrl(), getDerived()); 1415 // if (t.getCode().equals("Extension") && t.hasProfile() && !t.getProfile().contains(":")) { 1416 // lloydfix dt = 1417 // } 1418 if (dt == null) 1419 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants._HAS_CHILDREN__FOR_TYPE__IN_PROFILE__BUT_CANT_FIND_TYPE, diffMatches.get(0).getPath(), getDifferential().getElement().get(cursors.diffCursor).getPath(), profileUtilities.typeCode(outcome.getType()), getProfileName())); 1420 cursors.contextName = dt.getUrl(); 1421 int start = cursors.diffCursor; 1422 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), diffMatches.get(0).getPath() + ".")) 1423 cursors.diffCursor++; 1424 /* starting again on the data type, but skip the root */ 1425 ProfilePathProcessorState nc = new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start - 1, 1426 cursors.contextName, cursors.resultPathBase); 1427 this 1428 .incrementDebugIndent() 1429 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 1430 .withDiffLimit(cursors.diffCursor - 1) 1431 .withWebUrl(profileUtilities.getWebUrl(dt, getWebUrl())) 1432 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, 0)) 1433 .withContextPathSource(diffMatches.get(0).getPath()).withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()) 1434 .processPaths(nc, mapHelper, null); 1435 } 1436 } 1437 } 1438 } 1439 // --- 1440 diffpos++; 1441 } 1442 if (outcome.hasContentReference() && outcome.hasType()) { 1443 outcome.getType().clear(); 1444 } 1445 } 1446 cursors.baseCursor++; 1447 } 1448 1449 private boolean addToResult(ElementDefinition outcome) { 1450 return getResult().getElement().add(outcome); 1451 } 1452 1453 private void debugCheck(ElementDefinition outcome) { 1454 if (outcome.getPath().startsWith("List.") && "http://nictiz.nl/fhir/StructureDefinition/Bundle-MedicationOverview".equals(url)) { 1455 log.debug("wrong!"); 1456 } 1457 } 1458 1459 private void processPathWithSlicedBaseWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors, MappingAssistant mapHelper) { 1460 int start = 0; 1461 int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor); 1462 int newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(0)); 1463 ElementDefinition elementToRemove = null; 1464 boolean shortCut = (!typeList.isEmpty() && typeList.get(0).type != null) || (diffMatches.get(0).hasSliceName() && !diffMatches.get(0).hasSlicing()); 1465 // we come here whether they are sliced in the diff, or whether the short cut is used. 1466 if (shortCut) { 1467 // this is the short cut method, we've just dived in and specified a type slice. 1468 // in R3 (and unpatched R4, as a workaround right now... 1469 if (!VersionUtilities.isR4Plus(profileUtilities.getContext().getVersion()) || !profileUtilities.isNewSlicingProcessing()) { // newSlicingProcessing is a work around for editorial loop dependency 1470 // we insert a cloned element with the right types at the start of the diffMatches 1471 ElementDefinition ed = new ElementDefinition(); 1472 ed.setPath(profileUtilities.determineTypeSlicePath(diffMatches.get(0).getPath(), currentBasePath)); 1473 for (TypeSlice ts : typeList) 1474 ed.addType().setCode(ts.type); 1475 ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 1476 ed.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 1477 ed.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); 1478 ed.getSlicing().setOrdered(false); 1479 diffMatches.add(0, ed); 1480 getDifferential().getElement().add(newDiffCursor, ed); 1481 elementToRemove = ed; 1482 } else { 1483 // as of R4, this changed; if there's no slice, there's no constraint on the slice types, only one the type. 1484 // so the element we insert specifies no types (= all types) allowed in the base, not just the listed type. 1485 // see also discussion here: https://chat.fhir.org/#narrow/stream/179177-conformance/topic/Slicing.20a.20non-repeating.20element 1486 ElementDefinition ed = new ElementDefinition(); 1487 ed.setPath(profileUtilities.determineTypeSlicePath(diffMatches.get(0).getPath(), currentBasePath)); 1488 ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 1489 ed.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 1490 ed.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); 1491 ed.getSlicing().setOrdered(false); 1492 diffMatches.add(0, ed); 1493 getDifferential().getElement().add(newDiffCursor, ed); 1494 elementToRemove = ed; 1495 } 1496 } 1497 int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 1498 // the first element is setting up the slicing 1499 1500 if (diffMatches.get(0).getSlicing().hasOrdered()) { 1501 if (diffMatches.get(0).getSlicing().getOrdered()) { 1502 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGORDERED__TRUE, currentBasePath, getUrl())); 1503 } 1504 } 1505 if (diffMatches.get(0).getSlicing().hasDiscriminator()) { 1506 if (diffMatches.get(0).getSlicing().getDiscriminator().size() != 1) { 1507 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORCOUNT__1, currentBasePath, getUrl())); 1508 } 1509 if (diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getType() != ElementDefinition.DiscriminatorType.TYPE) { 1510 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORTYPE__TYPE, currentBasePath, getUrl())); 1511 } 1512 if (!"$this".equals(diffMatches.get(0).getSlicing().getDiscriminatorFirstRep().getPath())) { 1513 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__IN__TYPE_SLICING_WITH_SLICINGDISCRIMINATORPATH__THIS, currentBasePath, getUrl())); 1514 } 1515 } 1516 // check the slice names too while we're at it... 1517 for (TypeSlice ts : typeList) { 1518 if (ts.type != null) { 1519 String tn = profileUtilities.rootName(currentBasePath) + Utilities.capitalize(ts.type); 1520 if (!ts.defn.hasSliceName()) { 1521 ts.defn.setSliceName(tn); 1522 } else if (!ts.defn.getSliceName().equals(tn)) { 1523 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_NAME_MUST_BE__BUT_IS_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.getSliceName())); 1524 } 1525 if (!ts.defn.hasType()) { 1526 ts.defn.addType().setCode(ts.type); 1527 } else if (ts.defn.getType().size() > 1) { 1528 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_MORE_THAN_ONE_TYPE_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.typeSummary())); 1529 } else if (!ts.defn.getType().get(0).getCode().equals(ts.type)) { 1530 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ERROR_AT_PATH__SLICE_FOR_TYPE__HAS_WRONG_TYPE_, (!Utilities.noString(getContextPathSource()) ? getContextPathSource() : currentBasePath), tn, ts.defn.typeSummary())); 1531 } 1532 } 1533 } 1534 1535 // ok passed the checks. 1536 // copy the root diff, and then process any children it has 1537 ProfilePathProcessorState nc = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor, newDiffCursor, 1538 cursors.contextName, cursors.resultPathBase); 1539 ElementDefinition e = 1540 this 1541 .incrementDebugIndent() 1542 .withBaseLimit(newBaseLimit) 1543 .withDiffLimit(newDiffLimit) 1544 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches,0)) 1545 .withSlicing(new PathSlicingParams(true, null, currentBasePath)) 1546 .processPaths(nc, mapHelper, null); 1547 if (e == null) 1548 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, diffMatches.get(0).getPath())); 1549 // now set up slicing on the e (cause it was wiped by what we called. 1550 e.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent()); 1551 e.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this"); 1552 e.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED); // type slicing is always closed; the differential might call it open, but that just means it's not constraining the slices it doesn't mention 1553 e.getSlicing().setOrdered(false); 1554 start++; 1555 1556 String fixedType = null; 1557 List<BaseTypeSlice> baseSlices = profileUtilities.findBaseSlices(cursors.base, newBaseLimit); 1558 // now process the siblings, which should each be type constrained - and may also have their own children. they may match existing slices 1559 // now we process the base scope repeatedly for each instance of the item in the differential list 1560 for (int i = start; i < diffMatches.size(); i++) { 1561 String type = profileUtilities.determineFixedType(diffMatches, fixedType, i); 1562 // our processing scope for the differential is the item in the list, and all the items before the next one in the list 1563 if (diffMatches.get(i).getMin() > 0) { 1564 if (diffMatches.size() > i + 1) { 1565 throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.INVALID_SLICING__THERE_IS_MORE_THAN_ONE_TYPE_SLICE_AT__BUT_ONE_OF_THEM__HAS_MIN__1_SO_THE_OTHER_SLICES_CANNOT_EXIST, diffMatches.get(i).getPath(), diffMatches.get(i).getSliceName())); 1566 } 1567 fixedType = type; 1568 } 1569 newDiffCursor = getDifferential().getElement().indexOf(diffMatches.get(i)); 1570 newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor); 1571 int sStart = cursors.baseCursor; 1572 int sEnd = newBaseLimit; 1573 BaseTypeSlice bs = profileUtilities.chooseMatchingBaseSlice(baseSlices, type); 1574 if (bs != null) { 1575 sStart = bs.getStart(); 1576 sEnd = bs.getEnd(); 1577 bs.setHandled(true); 1578 } 1579 ProfilePathProcessorState nc2 = new ProfilePathProcessorState(cursors.baseSource, cursors.base, sStart, newDiffCursor, cursors.contextName, cursors.resultPathBase); 1580 this 1581 .incrementDebugIndent() 1582 .withBaseLimit(sEnd) 1583 .withDiffLimit(newDiffLimit) 1584 .withProfileName(getProfileName() + profileUtilities.pathTail(diffMatches, i)) 1585 .withSlicing(new PathSlicingParams(true, e, currentBasePath)) 1586 .processPaths(nc2, mapHelper, null); 1587 } 1588 if (elementToRemove != null) { 1589 getDifferential().getElement().remove(elementToRemove); 1590 newDiffLimit--; 1591 } 1592 if (fixedType != null) { 1593 for (Iterator<ElementDefinition.TypeRefComponent> iter = e.getType().iterator(); iter.hasNext(); ) { 1594 ElementDefinition.TypeRefComponent tr = iter.next(); 1595 if (!tr.getCode().equals(fixedType)) { 1596 iter.remove(); 1597 } 1598 } 1599 } 1600 for (BaseTypeSlice bs : baseSlices) { 1601 if (!bs.isHandled()) { 1602 // ok we gimme up a fake differential that says nothing, and run that against the slice. 1603 StructureDefinition.StructureDefinitionDifferentialComponent fakeDiff = new StructureDefinition.StructureDefinitionDifferentialComponent(); 1604 fakeDiff.getElementFirstRep().setPath(bs.getDefn().getPath()); 1605 ProfilePathProcessorState nc3 = new ProfilePathProcessorState(cursors.baseSource, cursors.base, bs.getStart(), 0, cursors.contextName, cursors.resultPathBase); 1606 this 1607 .incrementDebugIndent() 1608 .withDifferential(fakeDiff) 1609 .withBaseLimit(bs.getEnd()) 1610 .withDiffLimit(0) 1611 .withProfileName(getProfileName() + profileUtilities.tail(bs.getDefn().getPath())).withSlicing(new PathSlicingParams(true, e, currentBasePath)) 1612 .processPaths(nc3, mapHelper, null); 1613 1614 } 1615 } 1616 // ok, done with that - next in the base list 1617 cursors.baseCursor = baseSlices.get(baseSlices.size() - 1).getEnd() + 1; 1618 cursors.diffCursor = newDiffLimit + 1; 1619 //throw new Error("not done yet - slicing / types @ "+cpath); 1620 } 1621 1622 private void processPathWithSlicedBaseAndEmptyDiffMatches(ElementDefinition currentBase, String currentBasePath, List<ElementDefinition> diffMatches, ProfilePathProcessorState cursors, String path, MappingAssistant mapHelper) { 1623 if (profileUtilities.hasInnerDiffMatches(getDifferential(), path, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) { 1624 // so we just copy it in 1625 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), currentBase.copy(), true); 1626 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1627 profileUtilities.updateFromBase(outcome, currentBase, getSourceStructureDefinition().getUrl()); 1628 profileUtilities.markDerived(outcome); 1629 if (cursors.resultPathBase == null) 1630 cursors.resultPathBase = outcome.getPath(); 1631 else if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1632 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH)); 1633 debugCheck(outcome); 1634 addToResult(outcome); 1635 // the profile walks into this, so we need to as well 1636 // did we implicitly step into a new type? 1637 if (baseHasChildren(cursors.base, currentBase)) { // not a new type here 1638 ProfilePathProcessorState nc = new ProfilePathProcessorState(cursors.baseSource, cursors.base, cursors.baseCursor + 1, cursors.diffCursor, cursors.contextName, cursors.resultPathBase); 1639 this 1640 .incrementDebugIndent() 1641 .withSlicing(new PathSlicingParams()) 1642 .processPaths(nc, mapHelper, null); 1643 cursors.baseCursor = indexOfFirstNonChild(cursors.base, currentBase, cursors.baseCursor, getBaseLimit()); 1644 } else { 1645 StructureDefinition dt = profileUtilities.getTypeForElement(getDifferential(), cursors.diffCursor, getProfileName(), diffMatches, outcome, getWebUrl(), getDerived()); 1646 cursors.contextName = dt.getUrl(); 1647 int start = cursors.diffCursor; 1648 if (getDifferential().getElement().get(cursors.diffCursor).getPath().equals(currentBasePath)) { 1649 cursors.diffCursor++; 1650 } 1651 while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) { 1652 cursors.diffCursor++; 1653 } 1654 if (cursors.diffCursor > start) { 1655 /* starting again on the data type, but skip the root */ 1656 ProfilePathProcessorState nc2 = new ProfilePathProcessorState(dt, dt.getSnapshot(), 1 /* starting again on the data type, but skip the root */, start, 1657 cursors.contextName, cursors.resultPathBase); 1658 this 1659 .incrementDebugIndent() 1660 .withBaseLimit(dt.getSnapshot().getElement().size() - 1) 1661 .withDiffLimit(cursors.diffCursor - 1) 1662 .withWebUrl( profileUtilities.getWebUrl(dt, getWebUrl())) 1663 .withContextPathSource(currentBasePath) 1664 .withContextPathTarget(outcome.getPath()).withSlicing(new PathSlicingParams()) 1665 .processPaths(nc2, mapHelper, null); 1666 } 1667 } 1668 cursors.baseCursor++; 1669 } 1670 else { 1671 // the differential doesn't say anything about this item 1672 // copy across the currentbase, and all of its children and siblings 1673 while (cursors.baseCursor < cursors.base.getElement().size() && cursors.base.getElement().get(cursors.baseCursor).getPath().startsWith(path)) { 1674 ElementDefinition outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(cursors.baseCursor).copy(), true); 1675 outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource())); 1676 if (!outcome.getPath().startsWith(cursors.resultPathBase)) 1677 throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, getProfileName(), outcome.getPath(), cursors.resultPathBase)); 1678 debugCheck(outcome); 1679 profileUtilities.markExtensions(outcome, false, sourceStructureDefinition); 1680 addToResult(outcome); // so we just copy it in 1681 outcome.setUserData(UserDataNames.SNAPSHOT_BASE_MODEL, getSourceStructureDefinition().getUrl()); 1682 outcome.setUserData(UserDataNames.SNAPSHOT_BASE_PATH, cursors.resultPathBase); 1683 cursors.baseCursor++; 1684 } 1685 } 1686 } 1687 1688 private boolean oneMatchingElementInDifferential(boolean slicingDone, String path, List<ElementDefinition> diffMatches) { 1689 if (diffMatches.size() != 1) { 1690 return false; 1691 } 1692 if (slicingDone) { 1693 return true; 1694 } 1695 if (profileUtilities.isImplicitSlicing(diffMatches.get(0), path)) { 1696 return false; 1697 } 1698 return !(diffMatches.get(0).hasSlicing() 1699 || (profileUtilities.isExtension(diffMatches.get(0)) 1700 && diffMatches.get(0).hasSliceName())); 1701 } 1702 1703 1704}