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