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