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