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