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