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