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