protected EObject findCorrectContainer(
      org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedTerminal) {
    EObject container = expectedTerminal.getContainer();
    EClass ruleMetaclass = expectedTerminal.getTerminal().getRuleMetaclass();
    if (ruleMetaclass.isInstance(container)) {
      // container is correct for expected terminal
      return container;
    }
    // the container is wrong
    EObject parent = null;
    EObject previousParent = null;
    EObject correctContainer = null;
    EObject hookableParent = null;
    org.emftext.language.xpath3.resource.xpath3.grammar.Xpath3ContainmentTrace containmentTrace =
        expectedTerminal.getContainmentTrace();
    EClass startClass = containmentTrace.getStartClass();
    org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature currentLink = null;
    org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature previousLink = null;
    org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature[] containedFeatures =
        containmentTrace.getPath();
    for (int i = 0; i < containedFeatures.length; i++) {
      currentLink = containedFeatures[i];
      if (i > 0) {
        previousLink = containedFeatures[i - 1];
      }
      EClass containerClass = currentLink.getContainerClass();
      hookableParent = findHookParent(container, startClass, currentLink, parent);
      if (hookableParent != null) {
        // we found the correct parent
        break;
      } else {
        previousParent = parent;
        parent = containerClass.getEPackage().getEFactoryInstance().create(containerClass);
        if (parent != null) {
          if (previousParent == null) {
            // replace container for expectedTerminal with correctContainer
            correctContainer = parent;
          } else {
            // This assignment is only performed to get rid of a warning about a potential
            // null pointer access. Variable 'previousLink' cannot be null here, because it is
            // initialized for all loop iterations where 'i' is greather than 0 and for the
            // case where 'i' equals zero, this path is never executed, because
            // 'previousParent' is null in this case.
            org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature link =
                previousLink;
            org.emftext.language.xpath3.resource.xpath3.util.Xpath3EObjectUtil.setFeature(
                parent, link.getFeature(), previousParent, false);
          }
        }
      }
    }

    if (correctContainer == null) {
      correctContainer = container;
    }

    if (currentLink == null) {
      return correctContainer;
    }

    hookableParent = findHookParent(container, startClass, currentLink, parent);

    final EObject finalHookableParent = hookableParent;
    final EStructuralFeature finalFeature = currentLink.getFeature();
    final EObject finalParent = parent;
    if (parent != null && hookableParent != null) {
      expectedTerminal.setAttachmentCode(
          new Runnable() {

            public void run() {
              org.emftext.language.xpath3.resource.xpath3.util.Xpath3EObjectUtil.setFeature(
                  finalHookableParent, finalFeature, finalParent, false);
            }
          });
    }
    return correctContainer;
  }
  protected Collection<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal>
      deriveProposals(
          final org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal
              expectedTerminal,
          String content,
          org.emftext.language.xpath3.resource.xpath3.IXpath3TextResource resource,
          int cursorOffset) {
    org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement expectedElement =
        (org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement)
            expectedTerminal.getTerminal();
    if (expectedElement
        instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) {
      org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString csString =
          (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) expectedElement;
      return handleKeyword(
          expectedTerminal,
          csString,
          expectedTerminal.getPrefix(),
          expectedTerminal.getContainer());
    } else if (expectedElement
        instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal) {
      org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal
          expectedBooleanTerminal =
              (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal)
                  expectedElement;
      return handleBooleanTerminal(
          expectedTerminal, expectedBooleanTerminal, expectedTerminal.getPrefix());
    } else if (expectedElement
        instanceof
        org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal) {
      org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal
          expectedEnumerationTerminal =
              (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal)
                  expectedElement;
      return handleEnumerationTerminal(
          expectedTerminal, expectedEnumerationTerminal, expectedTerminal.getPrefix());
    } else if (expectedElement
        instanceof
        org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature) {
      final org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature
          expectedFeature =
              (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature)
                  expectedElement;
      final EStructuralFeature feature = expectedFeature.getFeature();
      final EClassifier featureType = feature.getEType();
      final EObject container = findCorrectContainer(expectedTerminal);

      // Here it gets really crazy. We need to modify the model in a way that reflects
      // the state the model would be in, if the expected terminal were present. After
      // computing the corresponding completion proposals, the original state of the
      // model is restored. This procedure is required, because different models can be
      // required for different completion situations. This can be particularly observed
      // when the user has not yet typed a character that starts an element to be
      // completed.
      final Collection<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal>
          proposals =
              new ArrayList<
                  org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal>();
      expectedTerminal.materialize(
          new Runnable() {

            public void run() {
              if (feature instanceof EReference) {
                EReference reference = (EReference) feature;
                if (featureType instanceof EClass) {
                  if (reference.isContainment()) {
                    // the FOLLOW set should contain only non-containment references
                    assert false;
                  } else {
                    proposals.addAll(
                        handleNCReference(
                            expectedTerminal,
                            container,
                            reference,
                            expectedTerminal.getPrefix(),
                            expectedFeature.getTokenName()));
                  }
                }
              } else if (feature instanceof EAttribute) {
                EAttribute attribute = (EAttribute) feature;
                if (featureType instanceof EEnum) {
                  EEnum enumType = (EEnum) featureType;
                  proposals.addAll(
                      handleEnumAttribute(
                          expectedTerminal,
                          expectedFeature,
                          enumType,
                          expectedTerminal.getPrefix(),
                          container));
                } else {
                  // handle EAttributes (derive default value depending on the type of the
                  // attribute, figure out token resolver, and call deResolve())
                  proposals.addAll(
                      handleAttribute(
                          expectedTerminal,
                          expectedFeature,
                          container,
                          attribute,
                          expectedTerminal.getPrefix()));
                }
              } else {
                // there should be no other subclass of EStructuralFeature
                assert false;
              }
            }
          });
      // Return the proposals that were computed in the closure call.
      return proposals;
    } else {
      // there should be no other class implementing IExpectedElement
      assert false;
    }
    return Collections.emptyList();
  }