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